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'
        }
    ]
}
Trung Tran
sumber
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.

Jonathan Eunice
sumber
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; }

Nina Scholz
sumber
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
ya, dengan Istirahat di Object Destrukturisasi .
Nina Scholz
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);
Timo
sumber
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})})
}, {})
metakungfu
sumber
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);

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

G.aziz
sumber
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);

SynCap
sumber
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);
nickxb
sumber
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 });
 }
Jonas Wilms
sumber
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));
exexzian
sumber
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

Wahinya Brian
sumber
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"));
Azayda
sumber
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);

BlackBeard
sumber
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');
sama vamsi
sumber
1

Coba saja ini berfungsi baik untuk saya.

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

agravat.in
sumber
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.

leonardofmed
sumber
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"
      }
   ]
}
Daniel Vukasovich
sumber
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>

Ori Drori
sumber
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.

adrien
sumber
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'));
AKelley
sumber
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;
});
Oluwafisayo Owolo
sumber
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)

Mohamed Abu Galala
sumber
-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())
Eugen Sunic
sumber
-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);

Rahul Sethi
sumber