JavaScript : 使值对数组形成值数组

JavaScript : Make an array of value pairs form an array of values

是否有一种优雅、实用的方法来转换这个数组:

[ 1, 5, 9, 21 ]

进入这个

[ [1, 5], [5, 9], [9, 21] ]

我知道我可以 forEach 数组并收集值以创建一个新数组。在 _.lodash 中是否有一种优雅的方法可以在不使用 forEach 的情况下做到这一点?

您可以映射拼接数组并检查索引。不为零则取前驱,否则取原数组的第一个元素

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; }

使用 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; }

如果您愿意使用另一个函数库 '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]); //=> []

这很容易用 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);

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

探索一种使用 reduce 和在函数闭包中定义的附加数组来存储待完成分区的方法可能会有用。

备注:

  • 不是单行本,但希望易于理解
  • part 在仅处理 2 个项目时不必是数组,但通过使用数组,我们扩展了方法以处理 n 大小的项目集
  • 如果你不是shift的粉丝,你可以使用slice的组合并重新定义part,但我认为这里是安全的。
  • 不返回 length 少于所需元素数的分区

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));

您可以只用一行 .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);

我确信在编程上有一种优雅的方式,但是,在数学上我不禁看到每个新对与原始数组的索引差异为 1。

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

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

这里是slide,它有两个参数来控制切片的大小和切片之间丢弃的元素数量

slide 与此处的其他答案不同,它为您提供了这些控制参数。这里的其他答案仅限于只生成 2 个切片,或者每次将切片递增 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]]