質問 リストを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];
}
_
ロダッシュはほとんどこれを行うことができます:
__.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]
_、not _[1, 2]
_、_[3, 4]
_。これは、OPが Pythonの元の質問 で求めていたものです。
「醜い」部分を関数にすると、見栄えが良くなります。
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)
Rubyでは、これは each_cons
:
(1..5).each_cons(2).to_a # => [[1, 2], [2, 3], [3, 4], [4, 5]]
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]]
この回答は、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の同等の機能とは異なり、LodashのdropRight
はHaskellの動作とは異なるため、Zip
が必要です。最短の配列ではなく、最長の配列の長さが使用されます。)
ラムダでも同じ:
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]]
ここに簡単なワンライナーがあります:
[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
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'])))
利点:
以下は、依存関係のない一般的な機能ソリューションです。
_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 の回答を確認してください。
残念ながら_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),
);
};
_
パラメータのスタイルをカレー化に変更することで、ペアワイズの定義はより見栄えが良くなります。
_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 ] ]
_
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)
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を渡します。レデューサーが指定されていない場合、デフォルトで、それぞれに2要素の配列を作成する関数になりますペア。
これが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
に解決されます。これはJavaScriptの「不正な」値であるため、ループが壊れます。
current = next ? next : this.shift();
next
が以前に設定されている場合は、これをcurrent
の値として使用します。これにより、アイテムごとに1回の反復が可能になるため、すべての要素を隣接する後続ノードと比較できます。
残りは簡単です。
私の2セント。基本的なスライス、ジェネレーターのバージョン。
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);
}
それが誰かを助けることを願っています! (好き)
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);
このために、すべてのパラメーターを指定してforEachを使用するだけです。
yourArray.forEach((current, idx, self) => {
if (let next = self[idx + 1]) {
//your code here
}
})