web-dev-qa-db-ja.com

オブジェクトの配列内のすべてのデータを合計して、オブジェクトの新しい配列にします

次のようなオブジェクトの配列があります。

var data = [{costOfAirtickets: 2500, costOfHotel: 1200},{costOfAirtickets: 1500, costOfHotel: 1000}]

そして、配列内の各要素を合計して、次のような配列を生成します。

var result = [{costOfAirtickets: 4000, costOfHotel: 3200}]

マップとリデュース機能を使用しましたが、次のように個々の要素を合計することしかできませんでした:

data.map(item => ite.costOfAirtickets).reduce((prev, next)=>prev + next); // 22

現時点では、これは単一の値を生成しますが、これは最初の説明では望んでいるものではありません。

これをJavascriptまたはおそらくlodashで行う方法はありますか。

27
Dave Kalu

for..inを使用してオブジェクトを反復処理し、reduceを使用して配列を反復処理します

var data = [{costOfAirtickets: 2500, costOfHotel: 1200},{costOfAirtickets: 1500, costOfHotel: 1000}];

var result = [data.reduce((acc, n) => {
  for (var prop in n) {
    if (acc.hasOwnProperty(prop)) acc[prop] += n[prop];
    else acc[prop] = n[prop];
  }
  return acc;
}, {})]
console.log(result)
18
Chris Li

Lodashを使用して、生活を簡素化します。

_const _ = require('lodash')
let keys = ['costOfAirtickets', 'costOfHotel']; 
let results = _.zipObject(keys, keys.map(key => _.sum( _.map(data, key))))
...
{ costOfAirtickets: 4000, costOfHotel: 2200 }
_

説明:

  • _.sum(_.map(data, key)):各配列の合計を生成します
  • __.zipObject_:結果を配列sumで圧縮します
  • __.map_として各キーの合計にkeys.map()を使用すると、順序が保証されません。

ドキュメンテーション:

7

配列をマップする必要はありません。実際には配列内の要素を減らすだけです。

const totalCostOfAirTickets: data.reduce((prev, next) => prev + next.costOfAirTickets, 0)
const totalCostOfHotel: data.reduce((prev, next) => prev + next.costOfHotel, 0)
const totals = [{totalCostOfAirTickets, totalCostOfHotel}]

一度に、次のようなことができます

const totals = data.reduce((prev, next) => { 
    prev.costOfAirTickets += next.costOfAirTickets; 
    prev.costOfHotel += next.costOfHotel; 
}, {costOfAirTickets: 0, costOfHotel: 0})
4
Simon

別の解決策は、Map not Array.prototype.map)を使用することです。これには、オブジェクトと比較していくつかの 注目すべき違いがあります

_var data = [{
  costOfAirtickets: 2500,
  costOfHotel: 1200
}, {
  costOfAirtickets: 1500,
  costOfHotel: 1000
}]

let sums = data.reduce((collection,rcd)=>{
  Object.entries(rcd).forEach(([key,value])=>{
      let sum = collection.get(key) || 0
      collection.set(key, sum + +value)
  })
  return collection
}, new Map())

console.log(...sums.entries())_

説明

外側のループ

上記は最初にdataメソッドを使用してreduce配列を反復処理します。その中の各オブジェクトは、レコードと呼ばれます。変数rcdを介してコード内で区別されます。

Reduceの各反復は、ループの次の反復の最初の引数として渡される値を返します。この場合、パラメーターcollectionはその引数を保持します。これは合計のセットです。

インナーループ

reduceループ内で、レコードの各キー/値ペアはforEachを使用して繰り返されます。キー/値のペアを取得するには、_Object.entries_メソッドを使用します。これらの引数を分解する配列を使用して、それぞれの変数keyおよびvalueに直接割り当てることができます。

値の取得/設定

プリミティブオブジェクトとは異なり、Mapには、get()およびset()を使用してエントリを取得および設定するための独自のメソッドがあります。したがって、最初にget()を使用して前の合計を取得します。設定されていない場合は、デフォルトで_0_になります。これが_|| 0_の機能です。その時点で、以前の合計が少なくとも0以上であると想定し、現在のキーの値をその上に追加できます。

マップの代替

Map がやや重い場合は、 Set などの同様のオブジェクトを使用することもできます。 (get()を除く)、またはプリミティブオブジェクト(つまり_{}_)を使用することもできます。

4
Mike

lodashアプローチです

__(data).flatMap(_.entries).groupBy(0).mapValues(v=>_.sumBy(v, 1)).value()
_

すべての一意のキーで合計されます。

_var data = [{costOfAirtickets: 2500, costOfHotel: 1200},{costOfAirtickets: 1500, costOfHotel: 1000}];

var res = _(data).flatMap(_.entries).groupBy(0).mapValues(v=>_.sumBy(v, 0)).value();

console.log(res);_
_<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>_

結果として配列が必要な場合は、.castArray()を使用してアンラップする前に、結果を_[...]_にラップするか、最後に.value()を使用します。

4

結果/削減値を作成するには、.reduce()の代わりに.map()メソッドを使用する必要があります。

let data = [
  {costOfAirtickets: 2500, costOfHotel: 1200},
  {costOfAirtickets: 1500, costOfHotel: 1000}
];

let result = data.reduce(
  (a, c) => (Object.keys(c).forEach(k => (a[k] = (a[k] || 0) + c[k])), a), {}
);

console.log(result);
4
Mohammad Usman

加算するオブジェクトを取得することで、配列を減らすことができます。

var data = [{ costOfAirtickets: 2500, costOfHotel: 1200 }, { costOfAirtickets: 1500, costOfHotel: 1000 }],
    keys = ['costOfAirtickets', 'costOfHotel'],
    sum = data.reduce((r, o) => {
        keys.forEach(k => r[k] += o[k]);
        return r;
    }, Object.assign(...keys.map(k => ({ [k]: 0 }))));

console.log(sum);
2
Nina Scholz

私の単純な答えは次のようになります。

_var data = [{costOfAirtickets: 2500, costOfHotel: 1200},
            {costOfAirtickets: 1500, costOfHotel: 1000}],
    res  = data.reduce((p,c) => Object.entries(c).reduce((r,[k,v]) => (r[k]+=v, r), p));
console.log(res);_

.reduce()の使用に関する注意:配列項目と累積値が同じタイプである場合、初期値をアキュムレーターとして使用しないことを検討し、以前の(p)で削減することができます。および現在の(c)要素。上記のスニペットの外部リデュースはこのタイプです。ただし、内部リデュースはオブジェクトを返すために、キー(k)値(v)ペアの配列を_[k,v]_として受け取ります。したがって、初期値(p)不可欠です。

結果は、オブジェクトとして累積された値です。配列で必要な場合は、_[res]_のような配列に入れてください。

1
Redu

mapオブジェクト。合計を計算し、別のオブジェクトに保存します。

var data = [{costOfAirtickets: 2500, costOfHotel: 1200},{costOfAirtickets: 1500, costOfHotel: 1000}];
var result = [];
var sum = 0;
var costsum = 0;
data.map(function(item, key){
  var cost = item;
  //nsole.log(cost);
  sum = sum + cost.costOfAirtickets;
  costsum = costsum + cost.costOfHotel;

});

result = [{costOfAirtickets:sum, costOfHotel:costsum}];

console.log(result);
1
chintuyadavsara

そのために、単純なforEach()ループを使用できます。

var data = [{costOfAirtickets: 2500, costOfHotel: 1200},{costOfAirtickets: 1500, costOfHotel: 1000}];
var res = [];
var tempObj = {};
data.forEach(({costOfAirtickets, costOfHotel}) => {
  tempObj['costOfAirtickets'] = (tempObj['costOfAirtickets'] || 0) + costOfAirtickets;
  tempObj['costOfHotel'] = (tempObj['costOfHotel'] || 0) + costOfHotel;
 });
res.Push(tempObj);
console.log(res);
1
Ankit Agarwal

以下のスニペットのように、reduceのみを使用してみてください。繰り返しを避けてください。以下のスニペットが役立つことを願っています!

var data = [{costOfAirtickets: 2500, costOfHotel: 1200},{costOfAirtickets: 1500, costOfHotel: 1000}]

var total = data.reduce(function (result,value,key) {
result['costOfAirtickets'] = result['costOfAirtickets']  + value['costOfAirtickets'];
result['costOfHotel'] = result['costOfHotel']  + value['costOfHotel'];

return result},{costOfAirtickets:0,costOfHotel:0});

console.log(total)
1
CRayen

これは私が考えることができる最も簡単なアプローチです

var data = [{costOfAirtickets: 2500, costOfHotel: 1200},{costOfAirtickets: 1500, costOfHotel: 1000}];
var sum ={};
for(var obj in data){
  for(var ele in data[obj]){
    if(!data[obj].hasOwnProperty(ele)) continue;
      if(sum[ele] === undefined){
        sum[ele] = data[obj][ele];
      }else{
        sum[ele] = sum[ele] + data[obj][ele];
      }
  }

}
var arr = [sum];
console.log(arr);
1
Armando Prieto
var data = [{costOfAirtickets: 2500, costOfHotel: 1200},{costOfAirtickets: 1500, costOfHotel: 1000}];

var result = [data.reduce((acc, n) => {
  for (var prop in n) {
    if (acc[prop]) acc[prop] += n[prop];
    else acc[prop] = n[prop];
  }
  return acc;
}, {})]
console.log(result)
0
asma khatoon

ロダッシュ

lodashreduce および _。mergeWith を使用すると、これは1つのライナーです。

var data = [{costOfAirtickets: 2500, costOfHotel: 1200}, {costOfAirtickets: 1500, costOfHotel: 1000}]

var result = _.reduce(data, (r,c) => _.mergeWith(r, c, (o = 0, s) => o + s), {})

console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>

ES6のみ

元の配列を変異させたくない場合ES6を利用できます reduceObject.entries =、および forEach このように:

var data = [{costOfAirtickets: 2500, costOfHotel: 1200}, {costOfAirtickets: 1500, costOfHotel: 1000}]

// One liner
var result1 = data.reduce((r, c) =>
  !Object.entries(c).forEach(([key, value]) => r[key] = (r[key] || 0) + value) && r, {})

// More readable
var result2 = data.reduce((r, c) => {
  Object.entries(c).forEach(([key, value]) => r[key] = (r[key] || 0) + value)
  return r
}, {})

console.log(result1)
console.log(result2)

初期のdata配列のmutatingを気にしない場合は、1つのライナーソリューションを使用できます。

var data = [{costOfAirtickets: 2500, costOfHotel: 1200}, {costOfAirtickets: 1500, costOfHotel: 1000}]

data.reduce((r, c) => !Object.entries(c).forEach(([key,value]) => r[key] += value) && r)

console.log(data[0])

より読みやすい:

var data = [{costOfAirtickets: 2500, costOfHotel: 1200}, {costOfAirtickets: 1500, costOfHotel: 1000}]

data.reduce((r, c) => {
  Object.entries(c).forEach(([key, value]) => r[key] += value)
  return r
})

console.log(data[0])

変化する例と変化しない例の唯一の違いは、reduce初期値です(また、合計のアキュムレータとして0インデックスを使用します)。変化するものにはinitial valueがありませんが、他のものでは空のオブジェクトリテラルで始まります。

結果を特に配列にする必要がある場合は、変化する例の場合は[data]を返し、純粋な例の場合は[result]を返します。

0
Akrion