在问题Iterate a list as pair (current, next) in Python中,OP有兴趣将Python列表迭代为一系列current, next
对。我有同样的问题,但我希望尽可能以最干净的方式在JavaScript中使用lodash。
使用简单的for
循环很容易做到这一点,但它并不是非常优雅。
for (var i = 0; i < arr.length - 1; i++) {
var currentElement = arr[i];
var nextElement = arr[i + 1];
}
Lodash几乎可以做到这一点:
_.forEach(_.zip(arr, _.rest(arr)), function(tuple) {
var currentElement = tuple[0];
var nextElement = tuple[1];
})
这个微妙的问题是,在最后一次迭代中,nextElement
将是undefined
。
当然,理想的解决方案只是pairwise
lodash函数,只在必要时循环。
_.pairwise(arr, function(current, next) {
// do stuff
});
是否有任何现有的库已经执行此操作?或者还有另一种在JavaScript中进行成对迭代的好方法,我还没有尝试过?
澄清:如果arr = [1, 2, 3, 4]
,那么我的pairwise
函数将迭代如下:[1, 2]
,[2, 3]
,[3, 4]
,而非[1, 2]
, [3, 4]
。这就是OP在the original question for Python中提出的要求。
答案 0 :(得分:7)
不确定为什么要这样,但你可以做出“丑陋的”#34;部分函数,然后它看起来不错:
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)
})
你甚至可以稍微修改它,以便能够迭代所有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)
答案 1 :(得分:6)
在Ruby中,这称为each_cons
:
(1..5).each_cons(2).to_a # => [[1, 2], [2, 3], [3, 4], [4, 5]]
那是proposed for Lodash,但被拒绝了;但是,npm上有一个each-cons模块:
const eachCons = require('each-cons')
eachCons([1, 2, 3, 4, 5], 2) // [[1, 2], [2, 3], [3, 4], [4, 5]]
aperture
中还有一个Ramda函数可以执行相同的操作:
const R = require('ramda')
R.aperture(2, [1, 2, 3, 4, 5]) // [[1, 2], [2, 3], [3, 4], [4, 5]]
答案 2 :(得分:3)
这个答案的灵感来自于我在Haskell中看到类似问题的答案:https://stackoverflow.com/a/4506000/5932012
我们可以使用Lodash的帮助者编写以下内容:
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]]
(与Haskell等价物不同,我们需要dropRight
,因为Lodash的zip
与Haskell的行为不同:它将使用最长数组的长度而不是最短的数组。)
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]]
虽然Ramda已经有一个涵盖这个名为aperture的功能。这稍微更通用,因为它允许您定义所需的连续元素数,而不是默认为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]]
答案 3 :(得分:2)
我们可以稍微包装Array.reduce来做到这一点,并保持一切清洁。 不需要循环索引/循环/外部库。
如果需要结果,只需创建一个数组来收集它。
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)
答案 4 :(得分:2)
这是一个简单的单行:
[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]))
或格式化:
[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]));
记录:
1 2
2 3
3 4
答案 5 :(得分:2)
这是一个没有任何依赖关系的通用功能解决方案:
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);
我知道看起来并不好看,但通过引入一些通用的实用功能,我们可以让它看起来更好看:
const sizedArray = (n) => Array(n).fill();
我可以将sizedArray
与forEach
结合使用times
来实现,但这样做效率很低。恕我直言,可以使用命令式代码来实现这样一个不言自明的功能:
const times = (n, cb) => {
while (0 < n--) {
cb();
}
}
如果您对更多核心解决方案感兴趣,请查看this answer。
不幸的是Array.fill
只接受一个值,而不是回调。所以Array(n).fill(array[Symbol.iterator]())
会在每个位置都放置相同的值。我们可以通过以下方式解决这个问题:
const fillWithCb = (n, cb) => sizedArray(n).map(cb);
最终实施:
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),
);
};
通过将参数样式更改为currying,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);
如果你运行这个,你得到:
> pairWise([1, 2, 3, 4, 5]);
// [ [ 1, 2 ], [ 2, 3 ], [ 3, 4 ], [ 4, 5 ] ]
答案 6 :(得分:1)
这是我的方法,使用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);
}
};
可以按如下方式调用:
// 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);
});
所以要打破它:
while (this.length) {
Array.prototype.shift
直接改变数组,因此当没有剩下任何元素时,长度显然将解析为0
。这是一个&#34; falsy&#34; JavaScript中的值,因此循环将会中断。
current = next ? next : this.shift();
如果先前已设置next
,请将此值用作current
的值。这允许每个项目进行一次迭代,以便可以将所有元素与其相邻的后继进行比较。
其余的很简单。
答案 7 :(得分:1)
使用iterables和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'])))
优势:
答案 8 :(得分:0)
d3.js提供了built-in版本的某些语言称为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])<>
对于指定数组中的每对相邻元素,依次调用传递给元素i和元素i-1的指定化简函数。如果未指定化简,则默认为创建两个元素的函数每对数组。
答案 9 :(得分:0)
我的两分钱。基本切片,生成器版本。
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);
}
答案 10 :(得分:0)
为此只需使用forEach及其所有参数:
yourArray.forEach((current, idx, self) => {
if (let next = self[idx + 1]) {
//your code here
}
})
答案 11 :(得分:0)
希望它可以帮助某人! (和喜欢)
arr = [1, 2, 3, 4];
output = [];
arr.forEach((val, index) => {
if (index < (arr.length - 1) && (index % 2) === 0) {
output.push([val, arr[index + 1]])
}
})
console.log(output);
答案 12 :(得分:0)
修改后的 zip:
const pairWise = a => a.slice(1).map((k,i) => [a[i], k]);
console.log(pairWise([1,2,3,4,5,6]));
输出:
<块引用>[ [ 1, 2 ], [ 2, 3 ], [ 3, 4 ], [ 4, 5 ], [ 5, 6 ] ]
通用版本是:
const nWise = n => a => a.slice(n).map((_,i) => a.slice(i, n+i));
console.log(nWise(3)([1,2,3,4,5,6,7,8]));
答案 13 :(得分:-1)
Lodash确实有一种方法可以让你这样做:https://lodash.com/docs#chunk
_.chunk(array, 2).forEach(function(pair) {
var first = pair[0];
var next = pair[1];
console.log(first, next)
})