Cara mengelompokkan berbagai objek dengan kunci


153

Adakah yang tahu cara (ganti jika mungkin juga) untuk mengelompokkan berbagai objek dengan kunci objek lalu membuat array objek baru berdasarkan pengelompokan? Sebagai contoh, saya memiliki berbagai objek mobil:

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'
    },
];

Saya ingin membuat array baru objek mobil yang dikelompokkan berdasarkan make:

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'
        }
    ]
}

1
Apakah kamu melihat groupBy?
SLaks

2
hasil Anda tidak valid
Nina Scholz

Apakah ada pendekatan serupa untuk mendapatkan Peta, bukan objek?
Andrea Bergonzo

Jawaban:


104

Jawaban Timo adalah bagaimana saya akan melakukannya. Sederhana _.groupBy, dan memungkinkan beberapa duplikasi pada objek dalam struktur yang dikelompokkan.

Namun OP juga meminta makekunci duplikat untuk dihapus. Jika Anda ingin pergi jauh-jauh:

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

console.log(grouped);

Hasil:

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

Jika Anda ingin melakukan ini menggunakan Underscore.js, perhatikan bahwa versinya _.mapValuesdipanggil _.mapObject.


278

Dalam Javascript biasa, Anda bisa menggunakan Array#reducedengan objek

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; }


1
bagaimana saya bisa mengulangi resulthasil?
Mounir Elfassi

1
Anda bisa mengambil entri dengan Object.entriesdan loop melalui pasangan kunci / nilai.
Nina Scholz

Apakah ada cara untuk menghapus makekumpulan data yang pernah dikelompokkan? Butuh ruang ekstra.
Mercurial


Untuk apa r dan berdiri? Apakah benar untuk berasumsi bahwa r adalah akumulator dan nilai saat ini?
Omar

68

Anda sedang mencari _.groupBy().

Menghapus properti yang Anda kelompokkan dari objek harus sepele jika diperlukan:

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>


Sebagai bonus, Anda mendapatkan sintaks yang lebih bagus dengan fungsi panah ES6:

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

18
Dan jika Anda menginginkannya tetap lebih pendek, var grouped = _.groupBy(cars, 'make');Tidak perlu fungsi sama sekali, jika accessor adalah nama properti yang sederhana.
Jonathan Eunice

1
Apa artinya '_'?
Adrian Grzywaczewski

@AdrianGrzywaczewski itu adalah konvensi default untuk spasi-nama 'lodash' atau 'garis bawah'. Sekarang librairies adalah modular itu tidak lagi diperlukan yaitu. npmjs.com/package/lodash.groupby
vilsbole

5
Dan bagaimana saya bisa menginterpretasikan hasilnya?
Luis Antonio Pestana

36

Versi singkat untuk mengelompokkan berbagai objek dengan kunci tertentu di es6:

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

Versi yang lebih panjang:

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

Tampaknya pertanyaan awal menanyakan bagaimana mengelompokkan mobil berdasarkan merek, tetapi mengabaikan merek di setiap grup. Jadi jawabannya akan terlihat seperti ini:

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

ini jelas bukan es5
Shinigami

Ini hanya berfungsi! Adakah yang bisa merinci fungsi pengurangan ini?
Jeevan

Saya menyukai kedua jawaban Anda, tetapi saya melihat keduanya memberikan bidang "make" sebagai anggota dari setiap array "make". Saya telah memberikan jawaban berdasarkan milik Anda di mana output yang dikirimkan sesuai dengan output yang diharapkan. Terima kasih!
Daniel Vukasovich

15

Ini adalah groupByfungsi Anda sendiri yang merupakan generalisasi kode dari: https://github.com/you-dont-need/You-Dont-Need-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);


15

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, car) => {

  const {
    model,
    year,
    make
  } = car;

  r[make] = [...r[make] || [], {
    model,
    year
  }];

  return r;
}, {});

console.log(cars);


8

Saya akan pergi REAL GROUP BYuntuk contoh JS Arrays persis sama dengan tugas ini di sini

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);


7

Anda dapat mencoba untuk memodifikasi objek di dalam fungsi yang disebut per iteration oleh _.groupFungsi. Perhatikan bahwa array sumber mengubah elemennya!

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

1
Sementara kode ini dapat menyelesaikan pertanyaan, termasuk penjelasan tentang bagaimana dan mengapa ini menyelesaikan masalah akan sangat membantu untuk meningkatkan kualitas posting Anda. Ingatlah bahwa Anda menjawab pertanyaan untuk pembaca di masa depan, bukan hanya orang yang bertanya sekarang! Harap edit jawaban Anda untuk menambahkan penjelasan, dan berikan indikasi batasan dan asumsi apa yang berlaku.
Makyen

Sepertinya jawaban terbaik bagi saya karena Anda hanya melalui array sekali untuk mendapatkan hasil yang diinginkan. Tidak perlu menggunakan fungsi lain untuk menghapus makeproperti, dan itu lebih mudah dibaca.
Carrm

7

Ini juga dimungkinkan dengan forloop sederhana :

 const result = {};

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

Dan mungkin lebih cepat juga, dan lebih sederhana. Saya telah memperluas cuplikan Anda menjadi sedikit lebih dinamis karena saya memiliki daftar panjang bidang dari tabel db yang tidak ingin saya ketik. Juga perhatikan Anda perlu mengganti const dengan membiarkan. for ( let { TABLE_NAME, ...fields } of source) { result[TABLE_NAME] = result[TABLE_NAME] || []; result[TABLE_NAME].push({ ...fields }); }
adrien

TIL, terima kasih! medium.com/@mautayro/…
adrien

5

Untuk kasus di mana kunci dapat menjadi nol dan kami ingin mengelompokkannya sebagai yang lain

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));

4

Buat metode yang dapat digunakan kembali

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
      }, {})
    };

Kemudian di bawah ini Anda dapat mengelompokkan berdasarkan kriteria apa pun

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


3

Versi prototipe menggunakan ES6 juga. Pada dasarnya ini menggunakan fungsi pengurangan untuk meneruskan akumulator dan item saat ini, yang kemudian menggunakan ini untuk membangun array "dikelompokkan" Anda berdasarkan kunci yang dilewatkan. bagian dalam dari pengurangan mungkin terlihat rumit tetapi pada dasarnya ini adalah pengujian untuk melihat apakah kunci dari objek yang dilewatkan ada dan jika kemudian tidak membuat array kosong dan menambahkan item saat ini ke array yang baru dibuat jika tidak menggunakan spread Operator melewatkan semua objek dari array kunci saat ini dan menambahkan item saat ini. Semoga ini bisa membantu seseorang !.

Array.prototype.groupBy = function(k) {
  return this.reduce((acc, item) => ((acc[item[k]] = [...(acc[item[k]] || []), item]), acc),{});
};

const projs = [
  {
    project: "A",
    timeTake: 2,
    desc: "this is a description"
  },
  {
    project: "B",
    timeTake: 4,
    desc: "this is a description"
  },
  {
    project: "A",
    timeTake: 12,
    desc: "this is a description"
  },
  {
    project: "B",
    timeTake: 45,
    desc: "this is a description"
  }
];

console.log(projs.groupBy("project"));

1

Anda juga dapat menggunakan array#forEach()metode seperti ini:

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);


1
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

Coba saja ini berfungsi baik untuk saya.

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


2
ReferenceError Uncaught: _ tidak didefinisikan - Anda harus jelas bahwa solusi Anda perlu menginstal perpustakaan pihak ke-3 hanya untuk menyelesaikan ini.
metakungfu

1
maaf, saya pikir semua orang tahu. Dudukan dan sebagian besar digunakan untuk lodash lib. jadi kamu perlu menggunakan lodash. mohon baca pertanyaannya sehingga Anda akan tahu dia sedang meminta lodash. Baiklah terima kasih. saya akan mengingat ini. dan tidak pernah lupa untuk menulis lib.
agravat.in

1

Saya membuat tolok ukur untuk menguji kinerja setiap solusi yang tidak menggunakan perpustakaan eksternal.

JSBen.ch

The reduce()pilihan, diposting oleh @Nina Scholz tampaknya menjadi salah satu yang optimal.


0

Saya suka jawaban @metakunfu, tetapi tidak memberikan hasil yang diharapkan. Berikut ini adalah pembaruan yang menyingkirkan "make" di payload JSON akhir.

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));

Keluaran:

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

0

Dengan lodash / fp Anda dapat membuat fungsi dengan _.flow()grup pertama itu dengan kunci, lalu memetakan setiap grup, dan menghapus kunci dari setiap item:

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

Membangun jawaban dengan @Jonas_Wilms jika Anda tidak ingin mengetikkan semua bidang Anda:

    var result = {};

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

Saya tidak membuat tolok ukur apa pun tetapi saya percaya menggunakan for for akan lebih efisien daripada apa pun yang disarankan dalam jawaban ini juga.


0
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

Array Objek yang Dikelompokkan dalam naskah dengan ini:

groupBy (list: any[], key: string): Map<string, Array<any>> {
    let map = new Map();
    list.map(val=> {
        if(!map.has(val[key])){
            map.set(val[key],list.filter(data => data[key] == val[key]));
        }
    });
    return map;
});

Ini terlihat tidak efisien ketika Anda melakukan pencarian untuk setiap tombol. Pencarian kemungkinan besar memiliki kompleksitas O (n).
Leukipp

0

Saya suka menulisnya tanpa ketergantungan / kompleksitas js sederhana murni.

const mp = {}
const cars = [
  {
    model: 'Imaginary space craft SpaceX model',
    year: '2025'
  },
  {
    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'
  }
]

cars.forEach(c => {
  if (!c.make) return // exit (maybe add them to a "no_make" category)

  if (!mp[c.make]) mp[c.make] = [{ model: c.model, year: c.year }]
  else mp[c.make].push({ model: c.model, year: c.year })
})

console.log(mp)


-1

Ini solusi lain untuk itu. Seperti yang diminta.

Saya ingin membuat array baru dari objek mobil yang dikelompokkan berdasarkan make:

function groupBy() {
  const key = 'make';
  return cars.reduce((acc, x) => ({
    ...acc,
    [x[key]]: (!acc[x[key]]) ? [{
      model: x.model,
      year: x.year
    }] : [...acc[x[key]], {
      model: x.model,
      year: x.year
    }]
  }), {})
}

Keluaran:

console.log('Grouped by make key:',groupBy())

-1

Ini adalah solusi yang terinspirasi dari Collectors.groupingBy () di Jawa:

function groupingBy(list, keyMapper) {
  return list.reduce((accummalatorMap, currentValue) => {
    const key = keyMapper(currentValue);
    if(!accummalatorMap.has(key)) {
      accummalatorMap.set(key, [currentValue]);
    } else {
      accummalatorMap.set(key, accummalatorMap.get(key).push(currentValue));
    }
    return accummalatorMap;
  }, new Map());
}

Ini akan memberikan objek Peta.

// Usage

const carMakers = groupingBy(cars, car => car.make);

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.