JavaScript:将值数组转换为值对数组

4
有没有一种优雅、实用的方法将这个数组: [ 1, 5, 9, 21 ] 转换为: [ [1, 5], [5, 9], [9, 21] ] 我知道可以使用 forEach 循环遍历数组并收集值来创建一个新数组。在不使用 forEach 的情况下,是否有一种优雅的方式在 _.lodash 中实现这个功能呢?

1
https://lodash.com/docs/4.17.4#chunk。顺便说一下,操作的名称通常称为“partition”,该链接是“lodash partition array”的第一个谷歌搜索结果。 - Jared Smith
3
@niceman建议将该函数称为“相邻对”,这个概念我之前用过。Jared Smith认为不是同一个东西。请看楼主的例子。 - aaaaaa
@aaaaaa 你说得对,我没有仔细看所需的输出。 - Jared Smith
1
@TreeNguyen 这不是完全重复的问题,而是一个类似的问题。那个问题只想要迭代,而这里的 OP 想要返回一个新数组。 - niceman
1
@aaaaaa 是啊,我一直在努力学习,所以采用艰难但可读性好的方式是我要追求的路径。否则,Ramda解决方案会非常完美。 - pyronaur
显示剩余3条评论
8个回答

6
您可以映射一个已切割的数组并检查索引。如果不为零,则取其前一项,否则取原始数组的第一项。

var array = [1, 5, 9, 21],
    result = array.slice(1).map((a, i, aa) => [i ? aa[i - 1] : array[0], a]);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

一个更简短的版本,正如 Bergi 所建议的:

var array = [1, 5, 9, 21],
    result = array.slice(1).map((a, i) => [array[i], a]);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }


谢谢,这看起来是最干净的方法,除了使用Ramda。 - pyronaur
为什么不直接使用(a, i) => [array[i], a]呢? - Bergi

5

使用map的快速方法如下:

const arr = [ 1, 5, 9, 21 ];

const grouped = arr.map((el, i) => [el, arr[i+1]]).slice(0, -1);

console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }


3
仍然可读,其他团队成员也能理解。 - Jankapunkt
@Norris,我终于移除了pop指令。现在的代码更加优雅了。 - Diego

2

这可以很容易地通过使用array.reduce来完成。以下操作使用数组作为聚合器,跳过第一个项目,然后对于之后的每个项目,将上一个项目和当前项目作为一对推送到数组中。

const arr = [ 1, 5, 9, 21 ];
const chunked = arr.reduce((p, c, i, a) => i === 0 ? p : (p.push([c, a[i-1]]), p), []);

console.log(chunked);

扩展版本应如下:

const arr = [1, 5, 9, 21];
const chunked = arr.reduce(function(previous, current, index, array) {
  if(index === 0){
    return previous;
  } else {
    previous.push([ current, array[index - 1]]);
    return previous;
  }
}, []);

console.log(chunked);


1
如果你愿意使用另一个函数库"ramda",aperture是你要寻找的函数。
从Ramda文档中获取的示例用法:
R.aperture(2, [1, 2, 3, 4, 5]); //=> [[1, 2], [2, 3], [3, 4], [4, 5]]
R.aperture(3, [1, 2, 3, 4, 5]); //=> [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
R.aperture(7, [1, 2, 3, 4, 5]); //=> []

Ramda 看起来非常不错。而且它正好符合我所需要的功能。 - pyronaur
我更喜欢它而不是lodash/fp - 但是维护者们不太活跃,并花费大量时间讨论而非编码。任你选择 :) - aaaaaa

0

我注意到当前的解决方案在某种程度上都是向前或向后查找(arr[i + 1]arr[i - 1])。

也许探索一种使用reduce和另一个数组的方法会很有用,该数组在函数闭包中定义,用于存储待完成的分区。

注:

  • 虽然不是单行代码,但希望易于理解
  • 当只处理2个项目时,part不必是一个数组,但使用数组可以将此方法扩展为适用于n个项的集合
  • 如果您不喜欢shift,可以使用slice的组合并重新定义part,但我认为在这里使用它是安全的。
  • 长度小于所需元素数量的分区将不返回

const partition = partitionSize => arr => {
  const part = [];
  
  return arr.reduce((parts, x) => {
    part.push(x);
    
    if (part.length === partitionSize) {
      parts.push(part.slice());
      part.shift();
    }
    
    return parts;
  }, []);
};

const makePairs = partition(2);
const makeTrios = partition(3);

const pairs = makePairs([1,2,3,4,5,6]);
const trios = makeTrios([1,2,3,4,5,6]);


console.log("partition(2)", JSON.stringify(pairs));
console.log("partition(3)", JSON.stringify(trios));


0
你可以使用只有一行的.reduce()而不需要初始值来执行以下操作;

var arr = [ 1, 5, 9, 21 ],
  pairs = arr.reduce((p,c,i) => i == 1 ? [[p,c]] : p.concat([[p[p.length-1][1],c]]));
console.log(pairs);


这在空数组上不起作用。为什么不使用初始参数?这是一个更短的一行代码。 - Bergi
@Bergi 当然,显然,如果你不想掉进“没有初始值的空数组减少”的陷阱中,最好采取预防措施。但是这只是一个模拟伪代码。然而,主要问题基本上是当输入为[1]时。 - Redu

0

我相信在编程上有一种优雅的方法,但数学上我不能帮助看到每对新数组与原始数组之间存在着索引差为1。

如果你(后来)需要将数组[1, 5, 9, 21, 33]转换为[[1, 9],[5, 21],[9, 33]],你可以利用索引之间的差为2的事实。

如果你为索引差为1创建代码,扩展它将会很容易。


0
这里是一个名为slide的函数,它有两个参数控制切片大小和每个切片之间要丢弃多少元素。 slide与其他答案不同,它提供了这些控制参数。其他答案只能产生固定大小或每次增加1的切片。

// take :: (Int, [a]) -> [a]
const take = (n, xs) =>
  xs.slice(0, n)

// drop :: (Int, [a]) -> [a]
const drop = (n, xs) =>
  xs.slice(n)
  
// slice :: (Int, Int, [a]) -> [[a]]
const slide = (m, n, xs) =>
  xs.length > m
    ? [take(m, xs), ...slide(m, n, drop(n, xs))]
    : [xs]
    
const arr = [0,1,2,3,4,5,6]

// log helper improves readability of output in stack snippet
const log = x => console.log(JSON.stringify(x))

log(slide(1, 1, arr))
// [[0],[1],[2],[3],[4],[5],[6]]

log(slide(1, 2, arr))
// [[0],[2],[4],[6]]

log(slide(2, 1, arr))
// [[0,1],[1,2],[2,3],[3,4],[4,5],[5,6]]

log(slide(2, 2, arr))
// [[0,1],[2,3],[4,5],[6]]

log(slide(3, 1, arr))
// [[0,1,2],[1,2,3],[2,3,4],[3,4,5],[4,5,6]]

log(slide(3, 2, arr))
// [[0,1,2],[2,3,4],[4,5,6]]

log(slide(3, 3, arr))
// [[0,1,2],[3,4,5],[6]] 

如果出于某种原因,您不想让 slide 包含 部分 切片(小于 m 的切片),我们可以将其编辑为以下内容。
// slice :: (Int, Int, [a]) -> [[a]]
const slide = (m, n, xs) =>
  xs.length > m
    ? [take(m, xs), ...slide(m, n, drop(n, xs))]
    : [] // <- return [] instead of [xs]

log(slide(2, 2, arr))
// now prints: [[0,1],[2,3],[4,5]]
// instead of: [[0,1],[2,3],[4,5],[6]]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接