web-dev-qa-db-ja.com

forEachを使用して部分合計のリストを作成する方法

私はこのような配列の配列を持っています:

changes = [ [1, 1, 1, -1], [1, -1, -1], [1, 1] ];

最後の値を追加して配列の次の値を取得したい

values = [ [1, 2, 3, 2], [1, 0, -1], [1, 2] ];

これまでのところ、私はforEachを使用しようとしました:

changes.forEach(change => {
    let i = changes.indexOf(change);
    let newValue = change[i] + change[i + 1]
});

私は正しい方向に進んでいると思いますが、このアプローチを機能させることはできません。

24
Team Cafe

合計を保存して値を追加できます。

var array = [[1, 1, 1, -1], [1, -1, -1], [1, 1]],
    result = array.map(a => a.map((s => v => s += v)(0)));

console.log(result);

forEachを使用する場合、オブジェクト参照と以前の値またはゼロを取得する必要があります。

var array = [[1, 1, 1, -1], [1, -1, -1], [1, 1]];

array.forEach(a => a.forEach((v, i, a) => a[i] = (a[i - 1] || 0) + v));

console.log(array);
27
Nina Scholz

map のバージョン。

const changes = [
  [1, 1, 1, -1],
  [1, -1, -1],
  [1, 1]
];

const values = changes.map(array => {
  let acc = 0;
  return array.map(v => acc += v);
});

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

そして、これはソース配列を変更しません。

13
Thomas

ジェネレーターの新しいESNext機能は、この点で優れています。

ここでは、再利用できるシンプルなsumpUpジェネレータを作成しました。

function* sumUp(a) {
  let sum = 0;
  for (const v of a) yield sum += v;
}

const changes = [ [1, 1, 1, -1], [1, -1, -1], [1, 1] ];
const values = changes.map(a => [...sumUp(a)]);
  
console.log(values);
4
Keith

配列の map 関数を使用できます

const changes = [ [1, 1, 1, -1], [1, -1, -1], [1, 1] ];    
const result = changes.map((v) => v.slice(0).map((t, i, arr) => i === 0 ? t : (arr[i] += arr[i - 1])))
console.log(changes);
console.log(result);

更新

slice を使用してアレイを複製します。これにより、元のアレイへの変更が防止されます。

3
Alexander
const changes = [ [1, 1, 1, -1], [1, -1, -1], [1, 1] ]
let values = []
changes.forEach(arr => {
  let accu = 0
  let nestedArr = []
  arr.forEach(n => {
    accu += n
    nestedArr.Push(accu)
  })
  values.Push(nestedArr)
})
console.log(values)
3
holydragon

以下は、配列の外部リストを反復処理する読みやすい方法です。内部配列のコピーが作成され、初期値([1、1、1、-1]など)が保持されます。次に、コピーされた配列の各値を反復処理し、元の配列の後に各インデックスに追加します。

var changes = [[1, 1, 1, -1], [1, -1, -1], [1, 1]];
changes.forEach(subArray => {
    var subArrayCopy = subArray.slice();        // Create a copy of the current sub array (i.e. subArrayCopy = [1, 1, 1, -1];)
    subArrayCopy.forEach((val, index) => {   // Iterate through each value in the copy
        for (var i = subArray.length - 1; i > index; i--) { // For each element from the end to the current index
            subArray[i] += val;  // Add the copy's current index value to the original array
        }
    });
})
console.log(changes);
2
Nick G

別の方法、

.map は、目的の結果を持つ新しい配列を返します。 .reduce アキュムレータとして配列を使用すると、サブ配列を生成できます。

var array = [[1, 1, 1, -1], [1, -1, -1], [1, 1]],
    result = array.map(a => a.reduce((ac, v, i) => {
      const lastVal = ac[i-1] || 0;
      return [...ac, lastVal + v];
    }, []));

console.log(result);

// shorter
result = array.map(a => a.reduce((ac, v, i) => [...ac, (ac[i-1] || 0) + v], []));
console.log(result);
2
R3tep

配列の配列がありますが、構成要素の配列はそれぞれ独立しているため、個別に取り組みましょう。 [1, 1, 1, -1]

「部分合計」という語句の使用は非常に有益です。 full sumは、reduceを使用して実装できます。

[1, 1, 1, -1].reduce((x, y) => x + y);
// 2

しかし、部分和の配列が必要です。これは、このreduceの使用法とよく似ていますが、最後に計算された結果だけを保持するのではなく、すべての中間値も保持します。他の言語では、これはscanと呼ばれます(cf. F#Haskell )。

一般的なscanのJavaScript実装は、おそらくreduceによく似ています。実際、これをreduceを使用して実装することができます。ほんの少しの追加作業が必要です。

function scan(array, callback) {
  const results = [array[0]];

  // reduce does all the heavy lifting for us, but we define a wrapper to pass to it.
  array.reduce((...args) => {
    // The wrapper forwards all those arguments to the callback, but captures the result...
    const result = callback(...args);
    // ...storing that intermediate result in our results array...
    results.Push(result);
    // ...then passes it back to reduce to continue what it was doing.
    return result;
  });

  return results;
}

// scan([1, 1, 1, -1], (x, y) => x + y) -> [1, 2, 3, 2]

より堅牢な実装は、標準ライブラリのreduceに、特に初期値の近くで実行されます。

function scan(array, callback, initialValue) {
  const results = [];

  const reducer = (...args) => {
    const result = callback(...args);
    results.Push(result);
    return result;
  };

  if (arguments.length === 2) {
    results.Push(array[0]);
    array.reduce(reducer);
  } else {
    results.Push(initialValue);
    array.reduce(reducer, initialValue);
  }

  return results;
}

まとめると、配列の配列に対してこれを行う場合は、map over scanになります。

[[1, 1, 1, -1], [1, -1, -1], [1, 1]].map(a => scan(a, (x, y) => x + y));
// [[1, 2, 3, 2], [1, 0, -1], [1, 2]]

コピーする必要はなく、注意する必要のある副作用もありません。起動すると、そこから便利な高階関数が得られます。

1
stuffy