web-dev-qa-db-ja.com

キーでオブジェクトの配列をグループ化する方法

誰かがオブジェクトキーでオブジェクトの配列をグループ化してから、そのグループ化に基づいて新しいオブジェクトの配列を作成する(できればlodashも)方法を知っていますか?例えば、私はcarオブジェクトの配列を持っています:

var cars = [
    {
        'make': 'audi',
        'model': 'r8',
        'year': '2012'
    }, {
        'make': 'audi',
        'model': 'rs5',
        'year': '2013'
    }, {
        'make': 'ford',
        'model': 'mustang',
        'year': '2012'
    }, {
        'make': 'ford',
        'model': 'fusion',
        'year': '2015'
    }, {
        'make': 'kia',
        'model': 'optima',
        'year': '2012'
    },
];

makeでグループ化された新しいcarオブジェクトの配列を作りたいです。

var cars = {
    'audi': [
        {
            'model': 'r8',
            'year': '2012'
        }, {
            'model': 'rs5',
            'year': '2013'
        },
    ],

    'ford': [
        {
            'model': 'mustang',
            'year': '2012'
        }, {
            'model': 'fusion',
            'year': '2015'
        }
    ],

    'kia': [
        {
            'model': 'optima',
            'year': '2012'
        }
    ]
}
80
Trung Tran

Timoの答え は私がそうする方法です。単純な_.groupBy、およびグループ化された構造内のオブジェクトでいくつかの重複を許可します。

しかし、OPは重複したmakeキーを削除することも求めました。あなたがずっと行きたいのなら:

var grouped = _.mapValues(_.groupBy(cars, 'make'),
                          clist => clist.map(car => _.omit(car, 'make')));

console.log(grouped);

収量:

{ audi:
   [ { model: 'r8', year: '2012' },
     { model: 'rs5', year: '2013' } ],
  ford:
   [ { model: 'mustang', year: '2012' },
     { model: 'fusion', year: '2015' } ],
  kia: [ { model: 'optima', year: '2012' } ] }

Underscore.jsを使用してこれを実行したい場合は、そのバージョンの_.mapValues_.mapObjectと呼ばれることに注意してください。

52
Jonathan Eunice

普通のJavascriptでは、オブジェクトに Array#reduce を使うことができます

var cars = [{ make: 'audi', model: 'r8', year: '2012' }, { make: 'audi', model: 'rs5', year: '2013' }, { make: 'ford', model: 'mustang', year: '2012' }, { make: 'ford', model: 'fusion', year: '2015' }, { make: 'kia', model: 'optima', year: '2012' }],
    result = cars.reduce(function (r, a) {
        r[a.make] = r[a.make] || [];
        r[a.make].Push(a);
        return r;
    }, Object.create(null));

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

あなたが探しているのは _.groupBy() です。

必要に応じて、グループ化しているプロパティをオブジェクトから削除するのは簡単です。

var cars = [{'make':'audi','model':'r8','year':'2012'},{'make':'audi','model':'rs5','year':'2013'},{'make':'ford','model':'mustang','year':'2012'},{'make':'ford','model':'fusion','year':'2015'},{'make':'kia','model':'optima','year':'2012'},];

var grouped = _.groupBy(cars, function(car) {
  return car.make;
});

console.log(grouped);
<script src='https://cdn.jsdelivr.net/lodash/4.17.2/lodash.min.js'></script>

おまけとして、ES6の矢印機能を使えば、さらに優れた構文が得られます。

const grouped = _.groupBy(cars, car => car.make);
45
Timo

Es6の特定のキーでオブジェクトの配列をグループ化するショートバージョン

result = array.reduce((h, obj) => Object.assign(h, { [obj.key]:( h[obj.key] || [] ).concat(obj) }), {})

長いバージョン:

result = array.reduce(function(h, obj) {
  h[obj.key] = (h[obj.key] || []).concat(obj);
  return h; 
}, {})

最初の質問では、車を製造元別にグループ化する方法を尋ねられていますが、各グループで製造元を省略しています。だから答えはこのようになります:

result = cars.reduce((h, {model,year,make}) => {
  return Object.assign(h, { [make]:( h[make] || [] ).concat({model,year})})
}, {})
23
metakungfu

これは、コードを一般化した独自のgroupBy関数です。 https://github.com/you-dont-need/You-Dont-Noded-Lodash-Underscore

function groupBy(xs, f) {
  return xs.reduce((r, v, i, a, k = f(v)) => ((r[k] || (r[k] = [])).Push(v), r), {});
}

const cars = [{ make: 'audi', model: 'r8', year: '2012' }, { make: 'audi', model: 'rs5', year: '2013' }, { make: 'ford', model: 'mustang', year: '2012' }, { make: 'ford', model: 'fusion', year: '2015' }, { make: 'kia', model: 'optima', year: '2012' }];

const result = groupBy(cars, (c) => c.make);
console.log(result);
10
cdiggins

_.groupBy funcによって反復ごとに呼び出される関数内のオブジェクトを変更しようとすることができます。ソース配列によって要素が変わることに注意してください。

var res = _.groupBy(cars,(car)=>{
    const makeValue=car.make;
    delete car.make;
    return makeValue;
})
console.log(res);
console.log(cars);
7
nickxbs

Keyがnullになる可能性があり、それらをその他にグループ化したい場合

var cars = [{'make':'audi','model':'r8','year':'2012'},{'make':'audi','model':'rs5','year':'2013'},{'make':'ford','model':'mustang','year':'2012'},{'make':'ford','model':'fusion','year':'2015'},{'make':'kia','model':'optima','year':'2012'},
            {'make':'kia','model':'optima','year':'2033'},
            {'make':null,'model':'zen','year':'2012'},
            {'make':null,'model':'blue','year':'2017'},

           ];


 result = cars.reduce(function (r, a) {
        key = a.make || 'others';
        r[key] = r[key] || [];
        r[key].Push(a);
        return r;
    }, Object.create(null));
3
exexzian
var cars = [{
  make: 'audi',
  model: 'r8',
  year: '2012'
}, {
  make: 'audi',
  model: 'rs5',
  year: '2013'
}, {
  make: 'ford',
  model: 'mustang',
  year: '2012'
}, {
  make: 'ford',
  model: 'fusion',
  year: '2015'
}, {
  make: 'kia',
  model: 'optima',
  year: '2012'
}].reduce((r, a) => {
  r[a.make] = [...r[a.make] || [], a];
  return r;
}, {});

console.log(cars);
3
G.aziz

単純なforループでも可能です。

 const result = {};

 for(const {make, model, year} of cars) {
   if(!result[make]) result[make] = [];
   result[make].Push({ model, year });
 }
3
Jonas Wilms

再利用できるメソッドを作成する

Array.prototype.groupBy = function(prop) {
      return this.reduce(function(groups, item) {
        const val = item[prop]
        groups[val] = groups[val] || []
        groups[val].Push(item)
        return groups
      }, {})
    };

それから、以下であなたはどんな基準でもグループ化することができます

const groupByMake = cars.groupBy('make');
        console.log(groupByMake);
var cars = [
    {
        'make': 'audi',
        'model': 'r8',
        'year': '2012'
    }, {
        'make': 'audi',
        'model': 'rs5',
        'year': '2013'
    }, {
        'make': 'ford',
        'model': 'mustang',
        'year': '2012'
    }, {
        'make': 'ford',
        'model': 'fusion',
        'year': '2015'
    }, {
        'make': 'kia',
        'model': 'optima',
        'year': '2012'
    },
];
  //re-usable method
Array.prototype.groupBy = function(prop) {
          return this.reduce(function(groups, item) {
                const val = item[prop]
                groups[val] = groups[val] || []
                groups[val].Push(item)
                return groups
          }, {})
        };
  
 // initiate your groupBy. Notice the recordset Cars and the field Make....
  const groupByMake = cars.groupBy('make');
                console.log(groupByMake);
    
    //At this point we have objects. You can use Object.keys to return an array
2
Wahinya Brian

私はJSアレイの例のためにREAL GROUP BYを全く同じこのタスク にここに残しておきます

const inputArray = [ 
    { Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" },
    { Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" },
    { Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" },
    { Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" },
    { Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" },
    { Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" },
    { Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" },
    { Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" }
];

var outObject = inputArray.reduce(function(a, e) {
  // GROUP BY estimated key (estKey), well, may be a just plain key
  // a -- Accumulator result object
  // e -- sequentally checked Element, the Element that is tested just at this itaration

  // new grouping name may be calculated, but must be based on real value of real field
  let estKey = (e['Phase']); 

  (a[estKey] ? a[estKey] : (a[estKey] = null || [])).Push(e);
  return a;
}, {});

console.log(outObject);

その他のコメントを見るよろしくお願いします。

2
SynCap

これを試してみてください。

let grouped = _.groupBy(cars, 'make');

1
agravat.in
function groupBy(data, property) {
  return data.reduce((acc, obj) => {
    const key = obj[property];
    if (!acc[key]) {
      acc[key] = [];
    }
    acc[key].Push(obj);
    return acc;
  }, {});
}
groupBy(people, 'age');
1
sama vamsi

このように array#forEach() メソッドを使うこともできます。

const cars = [{ make: 'audi', model: 'r8', year: '2012' }, { make: 'audi', model: 'rs5', year: '2013' }, { make: 'ford', model: 'mustang', year: '2012' }, { make: 'ford', model: 'fusion', year: '2015' }, { make: 'kia', model: 'optima', year: '2012' }];

let newcars = {}

cars.forEach(car => {
  newcars[car.make] ? // check if that array exists or not in newcars object
    newcars[car.make].Push({model: car.model, year: car.year})  // just Push
   : (newcars[car.make] = [], newcars[car.make].Push({model: car.model, year: car.year})) // create a new array and Push
})

console.log(newcars);
0
BlackBeard
const reGroup = (list, key) => {
    const newGroup = {};
    list.forEach(item => {
        const newItem = Object.assign({}, item);
        delete newItem[key];
        newGroup[item[key]] = newGroup[item[key]] || [];
        newGroup[item[key]].Push(newItem);
    });
    return newGroup;
};
const animals = [
  {
    type: 'dog',
    breed: 'puddle'
  },
  {
    type: 'dog',
    breed: 'labradoodle'
  },
  {
    type: 'cat',
    breed: 'siamese'
  },
  {
    type: 'dog',
    breed: 'french bulldog'
  },
  {
    type: 'cat',
    breed: 'mud'
  }
];
console.log(reGroup(animals, 'type'));
const cars = [
  {
      'make': 'audi',
      'model': 'r8',
      'year': '2012'
  }, {
      'make': 'audi',
      'model': 'rs5',
      'year': '2013'
  }, {
      'make': 'ford',
      'model': 'mustang',
      'year': '2012'
  }, {
      'make': 'ford',
      'model': 'fusion',
      'year': '2015'
  }, {
      'make': 'kia',
      'model': 'optima',
      'year': '2012'
  },
];

console.log(reGroup(cars, 'make'));
0
AKelley

すべてのフィールドに入力したくない場合は、@ Jonas_Wilmsによる answer を基に作成します。

    var result = {};

    for ( let { first_field, ...fields } of your_data ) 
    { 
       result[first_field] = result[first_field] || [];
       result[first_field].Push({ ...fields }); 
    }

私はベンチマークを作成しませんでしたが、forループを使用することは この回答 で提案されているものよりも効率的であると考えています。

0
adrien

Lodash/fpを使うと、_.flow()を使って、最初にキーでグループ化する関数を作成し、それから各グループをマッピングし、各項目からキーを省略することができます。

const { flow, groupBy, mapValues, map, omit } = _;

const groupAndOmitBy = key => flow(
  groupBy(key),
  mapValues(map(omit(key)))
);

const cars = [{ make: 'audi', model: 'r8', year: '2012' }, { make: 'audi', model: 'rs5', year: '2013' }, { make: 'ford', model: 'mustang', year: '2012' }, { make: 'ford', model: 'fusion', year: '2015' }, { make: 'kia', model: 'optima', year: '2012' }];

const groupAndOmitMake = groupAndOmitBy('make');

const result = groupAndOmitMake(cars);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src='https://cdn.jsdelivr.net/g/lodash@4(lodash.min.js+lodash.fp.min.js)'></script>
0
Ori Drori

私は@metakunfuの回答が好きでした、しかしそれは期待された出力を正確に提供しません。これが最終的なJSONペイロードの「make」を取り除く最新版です。

var cars = [
    {
        'make': 'audi',
        'model': 'r8',
        'year': '2012'
    }, {
        'make': 'audi',
        'model': 'rs5',
        'year': '2013'
    }, {
        'make': 'ford',
        'model': 'mustang',
        'year': '2012'
    }, {
        'make': 'ford',
        'model': 'fusion',
        'year': '2015'
    }, {
        'make': 'kia',
        'model': 'optima',
        'year': '2012'
    },
];

result = cars.reduce((h, car) => Object.assign(h, { [car.make]:( h[car.make] || [] ).concat({model: car.model, year: car.year}) }), {})

console.log(JSON.stringify(result));

出力:

{  
   "audi":[  
      {  
         "model":"r8",
         "year":"2012"
      },
      {  
         "model":"rs5",
         "year":"2013"
      }
   ],
   "ford":[  
      {  
         "model":"mustang",
         "year":"2012"
      },
      {  
         "model":"fusion",
         "year":"2015"
      }
   ],
   "kia":[  
      {  
         "model":"optima",
         "year":"2012"
      }
   ]
}
0