在 JavaScript 中将数组作为对(当前,下一个)迭代
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/31973278/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Iterate an array as a pair (current, next) in JavaScript
提问by therealrootuser
In the question Iterate a list as pair (current, next) in Python, the OP is interested in iterating a Python list as a series of current, next
pairs. I have the same problem, but I'd like to do it in JavaScript in the cleanest way possible, perhaps using lodash.
在问题Iterate a list as pair (current, next) in Python 中,OP 有兴趣将 Python 列表迭代为一系列current, next
对。我有同样的问题,但我想以最简洁的方式在 JavaScript 中完成它,也许使用lodash。
It is easy to do this with a simple for
loop, but it doesn't feel very elegant.
用一个简单的for
循环很容易做到这一点,但感觉不是很优雅。
for (var i = 0; i < arr.length - 1; i++) {
var currentElement = arr[i];
var nextElement = arr[i + 1];
}
Lodash almost can do this:
Lodash 几乎可以做到这一点:
_.forEach(_.zip(arr, _.rest(arr)), function(tuple) {
var currentElement = tuple[0];
var nextElement = tuple[1];
})
The subtle problem with this that on the last iteration, nextElement
will be undefined
.
在最后一次迭代中,这个微妙的问题nextElement
将是undefined
.
Of course the ideal solution would simply be a pairwise
lodash function that only looped as far as necessary.
当然,理想的解决方案只是一个pairwise
lodash 函数,它只在必要时循环。
_.pairwise(arr, function(current, next) {
// do stuff
});
Are there any existing libraries that do this already? Or is there another nice way to do pairwise iteration in JavaScript that I haven't tried?
是否有任何现有的图书馆已经这样做了?或者还有另一种我没有尝试过的在 JavaScript 中进行成对迭代的好方法吗?
Clarification: If arr = [1, 2, 3, 4]
, then my pairwise
function would iterate as follows: [1, 2]
, [2, 3]
, [3, 4]
, not [1, 2]
, [3, 4]
. This is what the OP was asking about in the original question for Python.
说明:如果arr = [1, 2, 3, 4]
,那么我的pairwise
函数将迭代如下:[1, 2]
, [2, 3]
, [3, 4]
, not [1, 2]
, [3, 4]
。这就是 OP 在Python 的原始问题中询问的内容。
采纳答案by juvian
Just make the "ugly" part into a function and then it looks nice:
只需将“丑陋”部分变成一个函数,然后它看起来就不错了:
arr = [1, 2, 3, 4];
function pairwise(arr, func){
for(var i=0; i < arr.length - 1; i++){
func(arr[i], arr[i + 1])
}
}
pairwise(arr, function(current, next){
console.log(current, next)
})
You can even slightly modify it to be able to make iterate all i, i+n pairs, not just the next one:
您甚至可以稍微修改它,以便能够迭代所有 i, i+n 对,而不仅仅是下一个:
function pairwise(arr, func, skips){
skips = skips || 1;
for(var i=0; i < arr.length - skips; i++){
func(arr[i], arr[i + skips])
}
}
pairwise([1, 2, 3, 4, 5, 6, 7], function(current,next){
console.log(current, next) // displays (1, 3), (2, 4), (3, 5) , (4, 6), (5, 7)
}, 2)
回答by chocolateboy
In Ruby, this is called each_cons
:
在 Ruby 中,这称为each_cons
:
(1..5).each_cons(2).to_a # => [[1, 2], [2, 3], [3, 4], [4, 5]]
It was proposed for Lodash, but rejected; however, there's an each-consmodule on npm:
它被提议用于 Lodash,但被拒绝了;但是,npm 上有一个each-cons模块:
const eachCons = require('each-cons')
eachCons([1, 2, 3, 4, 5], 2) // [[1, 2], [2, 3], [3, 4], [4, 5]]
There's also an aperture
function in Ramdawhich does the same thing:
Ramda 中还有一个aperture
函数可以做同样的事情:
const R = require('ramda')
R.aperture(2, [1, 2, 3, 4, 5]) // [[1, 2], [2, 3], [3, 4], [4, 5]]
回答by Oliver Joseph Ash
This answer is inspired by an answer I saw to a similar question but in Haskell: https://stackoverflow.com/a/4506000/5932012
这个答案的灵感来自我在 Haskell 中看到的类似问题的答案:https: //stackoverflow.com/a/4506000/5932012
We can use helpers from Lodash to write the following:
我们可以使用 Lodash 的 helpers 来编写以下内容:
const zipAdjacent = function<T> (ts: T[]): [T, T][] {
return zip(dropRight(ts, 1), tail(ts));
};
zipAdjacent([1,2,3,4]); // => [[1,2], [2,3], [3,4]]
(Unlike the Haskell equivalent, we need dropRight
because Lodash's zip
behaves differently to Haskell's`: it will use the length of the longest array instead of the shortest.)
(与 Haskell 的等价物不同,我们需要,dropRight
因为 Lodash 的zip
行为与 Haskell的不同:它将使用最长数组的长度而不是最短数组的长度。)
The same in Ramda:
在 Ramda 中也是如此:
const zipAdjacent = function<T> (ts: T[]): [T, T][] {
return R.zip(ts, R.tail(ts));
};
zipAdjacent([1,2,3,4]); // => [[1,2], [2,3], [3,4]]
Although Ramda already has a function that covers this called aperture. This is slightly more generic because it allows you to define how many consecutive elements you want, instead of defaulting to 2:
虽然 Ramda 已经有一个函数来覆盖这个叫做孔径。这稍微更通用,因为它允许您定义所需的连续元素数量,而不是默认为 2:
R.aperture(2, [1,2,3,4]); // => [[1,2], [2,3], [3,4]]
R.aperture(3, [1,2,3,4]); // => [[1,2,3],[2,3,4]]
回答by salomvary
Another solution using iterablesand generator functions:
function * pairwise (iterable) {
const iterator = iterable[Symbol.iterator]()
let current = iterator.next()
let next = iterator.next()
while (!current.done) {
yield [current.value, next.value]
current = next
next = iterator.next()
}
}
console.log(...pairwise([]))
console.log(...pairwise(['apple']))
console.log(...pairwise(['apple', 'orange', 'kiwi', 'banana']))
console.log(...pairwise(new Set(['apple', 'orange', 'kiwi', 'banana'])))
Advantages:
好处:
- Works on all iterables, not only arrays (eg. Sets).
- Does not create any intermediate or temporary array.
- Lazy evaluated, works efficiently on very large iterables.
- 适用于所有可迭代对象,而不仅仅是数组(例如集合)。
- 不创建任何中间或临时数组。
- 懒惰评估,在非常大的可迭代对象上有效工作。
回答by fodma1
Here's a generic functional solution without any dependencies:
这是一个没有任何依赖项的通用功能解决方案:
const nWise = (n, array) => {
iterators = Array(n).fill()
.map(() => array[Symbol.iterator]());
iterators
.forEach((it, index) => Array(index).fill()
.forEach(() => it.next()));
return Array(array.length - n + 1).fill()
.map(() => (iterators
.map(it => it.next().value);
};
const pairWise = (array) => nWise(2, array);
I know doesn't look nice at all but by introducing some generic utility functions we can make it look a lot nicer:
我知道一点也不好看,但通过引入一些通用的实用函数,我们可以让它看起来更好看:
const sizedArray = (n) => Array(n).fill();
I could use sizedArray
combined with forEach
for times
implementation, but that'd be an inefficient implementation. IMHO it's ok to use imperative code for such a self-explanatory function:
我可以sizedArray
将 withforEach
用于times
实现,但这将是一个低效的实现。恕我直言,可以为这样一个不言自明的函数使用命令式代码:
const times = (n, cb) => {
while (0 < n--) {
cb();
}
}
If you're interested in more hardcore solutions, please check thisanswer.
如果您对更多核心解决方案感兴趣,请查看此答案。
Unfortunately Array.fill
only accepts a single value, not a callback. So Array(n).fill(array[Symbol.iterator]())
would put the same value in every position. We can get around this the following way:
不幸的是Array.fill
只接受一个值,而不是一个回调。所以Array(n).fill(array[Symbol.iterator]())
会在每个位置放置相同的值。我们可以通过以下方式解决这个问题:
const fillWithCb = (n, cb) => sizedArray(n).map(cb);
The final implementation:
最后的实现:
const nWise = (n, array) => {
iterators = fillWithCb(n, () => array[Symbol.iterator]());
iterators.forEach((it, index) => times(index, () => it.next()));
return fillWithCb(
array.length - n + 1,
() => (iterators.map(it => it.next().value),
);
};
By changing the parameter style to currying, the definition of pairwise would look a lot nicer:
通过将参数样式更改为柯里化,pairwise 的定义看起来会好很多:
const nWise = n => array => {
iterators = fillWithCb(n, () => array[Symbol.iterator]());
iterators.forEach((it, index) => times(index, () => it.next()));
return fillWithCb(
array.length - n + 1,
() => iterators.map(it => it.next().value),
);
};
const pairWise = nWise(2);
And if you run this you get:
如果你运行它,你会得到:
> pairWise([1, 2, 3, 4, 5]);
// [ [ 1, 2 ], [ 2, 3 ], [ 3, 4 ], [ 4, 5 ] ]
回答by DarkKnight
We can wrap Array.reducea little to do this, and keep everything clean. Loop indices / loops / external libraries are not required.
我们可以稍微包装Array.reduce来做到这一点,并保持一切干净。不需要循环索引/循环/外部库。
If the result is required, just create an array to collect it.
如果需要结果,只需创建一个数组来收集它。
function pairwiseEach(arr, callback) {
arr.reduce((prev, current) => {
callback(prev, current)
return current
})
}
function pairwise(arr, callback) {
const result = []
arr.reduce((prev, current) => {
result.push(callback(prev, current))
return current
})
return result
}
const arr = [1, 2, 3, 4]
pairwiseEach(arr, (a, b) => console.log(a, b))
const result = pairwise(arr, (a, b) => [a, b])
const output = document.createElement('pre')
output.textContent = JSON.stringify(result)
document.body.appendChild(output)
回答by Robert Mitchell
Here's a simple one-liner:
这是一个简单的单行:
[1,2,3,4].reduce((acc, v, i, a) => { if (i < a.length - 1) { acc.push([a[i], a[i+1]]) } return acc; }, []).forEach(pair => console.log(pair[0], pair[1]))
Or formatted:
或格式化:
[1, 2, 3, 4].
reduce((acc, v, i, a) => {
if (i < a.length - 1) {
acc.push([a[i], a[i + 1]]);
}
return acc;
}, []).
forEach(pair => console.log(pair[0], pair[1]));
which logs:
其中日志:
1 2
2 3
3 4
回答by Xavier Guihot
d3.jsprovides a built-inversion of what is called in certain languages a sliding
:
d3.js提供了在某些语言中称为a 的内置版本sliding
:
console.log(d3.pairs([1, 2, 3, 4])); // [[1, 2], [2, 3], [3, 4]]
<script src="http://d3js.org/d3.v5.min.js"></script>
#d3.pairs(array[, reducer]) <>
For each adjacent pair of elements in the specified array, in order, invokes the specified reducer function passing the element i and element i - 1. If a reducer is not specified, it defaults to a function which creates a two-element array for each pair.
#d3.pairs(array[, reducer]) <>
对于指定数组中的每对相邻元素,依次调用指定的 reducer 函数,传递元素 i 和元素 i - 1。一对。
回答by James Wright
Here's my approach, using Array.prototype.shift
:
这是我的方法,使用Array.prototype.shift
:
Array.prototype.pairwise = function (callback) {
const copy = [].concat(this);
let next, current;
while (copy.length) {
current = next ? next : copy.shift();
next = copy.shift();
callback(current, next);
}
};
This can be invoked as follows:
这可以调用如下:
// output:
1 2
2 3
3 4
4 5
5 6
[1, 2, 3, 4, 5, 6].pairwise(function (current, next) {
console.log(current, next);
});
So to break it down:
所以分解它:
while (this.length) {
Array.prototype.shift
directly mutates the array, so when no elements are left, length will obviously resolve to 0
. This is a "falsy" value in JavaScript, so the loop will break.
Array.prototype.shift
直接改变数组,所以当没有元素剩下时,长度显然会解析为0
。这是 JavaScript 中的“假”值,因此循环将中断。
current = next ? next : this.shift();
If next
has been set previously, use this as the value of current
. This allows for one iteration per item so that all elements can be compared against their adjacent successor.
如果next
之前已设置,则将其用作 的值current
。这允许每个项目进行一次迭代,以便所有元素都可以与其相邻的后继元素进行比较。
The rest is straightforward.
其余的很简单。
回答by Константин Ван
My two cents. Basic slicing, generator version.
我的两分钱。基本切片,生成器版本。
function* generate_windows(array, window_size) {
const max_base_index = array.length - window_size;
for(let base_index = 0; base_index <= max_base_index; ++base_index) {
yield array.slice(base_index, base_index + window_size);
}
}
const windows = generate_windows([1, 2, 3, 4, 5, 6, 7, 8, 9], 3);
for(const window of windows) {
console.log(window);
}