简体   繁体   中英

Create array of objects by key from array of objects

There are a lot of questions like this on here, but I couldn't find one that matched my needs. I'm looking for a relatively simple solution on how to stack objects in an array into new arrays based on a key.

In the example data we're grouping the objects by their "ship" key.

Original data:

 var myObjArray = [
    {
        name:'Malcolm Reynolds',
        ship:'Serenity'
    },
    {
        name: 'Carmen Ibanez',
        ship: 'Rodger Young',
    },
    {
        name: 'Zander Barcalow',
        ship: 'Rodger Young',
    },
    {
        name:'Hoban Washburne',
        ship:'Serenity'
    },
    {
        name:'James Kirk',
        ship:'USS Enterprise'
    }
];

Restructured Data:

    var myNewObjArray = [
    [{
        name:'Malcolm Reynolds',
        ship:'Serenity'
    },
    {
        name:'Hoban Washburne',
        ship:'Serenity'
    }],
    [{
        name: 'Carmen Ibanez',
        ship: 'Rodger Young',
    },
    {
        name: 'Zander Barcalow',
        ship: 'Rodger Young',
    }],
    {
        name:'James Kirk', // optionally also stick in an array
        ship:'USS Enterprise'
    }
];

If anyone has a solution for this I'd appreciate it, my current attempt is sloppy to say the least.

You could take an object for and the ship value as key for the same group. For the result take only the values of the object.

 var data = [{ name: 'Malcolm Reynolds', ship: 'Serenity' }, { name: 'Carmen Ibanez', ship: 'Rodger Young' }, { name: 'Zander Barcalow', ship: 'Rodger Young' }, { name: 'Hoban Washburne', ship: 'Serenity' }, { name: 'James Kirk', ship: 'USS Enterprise' }], grouped = Object.values(data.reduce((r, o) => { if (!r[o.ship]) { r[o.ship] = o; return r; } if (!Array.isArray(r[o.ship])) r[o.ship] = [r[o.ship]]; r[o.ship].push(o); return r; }, {})); console.log(grouped); 
 .as-console-wrapper { max-height: 100% !important; top: 0; } 

An approach with a Map

 var data = [{ name: 'Malcolm Reynolds', ship: 'Serenity' }, { name: 'Carmen Ibanez', ship: 'Rodger Young' }, { name: 'Zander Barcalow', ship: 'Rodger Young' }, { name: 'Hoban Washburne', ship: 'Serenity' }, { name: 'James Kirk', ship: 'USS Enterprise' }], grouped = Array.from( data .reduce((m, o) => m.set(o.ship, [...(m.get(o.ship) || []), o]), new Map) .values(), a => a.length === 1 ? a[0] : a ); console.log(grouped); 
 .as-console-wrapper { max-height: 100% !important; top: 0; } 

Find and deduplicate names of ships, then find personnel for every ship.

 const myObjArray = [ { name:'Malcolm Reynolds', ship:'Serenity' }, { name: 'Carmen Ibanez', ship: 'Rodger Young', }, { name: 'Zander Barcalow', ship: 'Rodger Young', }, { name:'Hoban Washburne', ship:'Serenity' }, { name:'James Kirk', ship:'USS Enterprise' } ]; const ships = myObjArray.map(({ship}) => ship).filter((ship, i, arr) => arr.indexOf(ship) === i); const personnelArray = ships.map(ship => myObjArray.filter(entry => entry.ship === ship)); console.log(personnelArray); 

Another clean and elegant solution would be working with Lodash .

First, group by the array with the relevant key.Then,Get the values from the object.

From the docs :

Creates an object composed of keys generated from the results of running each element of collection thru iteratee. The order of grouped values is determined by the order they occur in collection. The corresponding value of each key is an array of elements responsible for generating the key. The iteratee is invoked with one argument: (value).

 const myObjArray = [ { name:'Malcolm Reynolds', ship:'Serenity' }, { name: 'Carmen Ibanez', ship: 'Rodger Young', }, { name: 'Zander Barcalow', ship: 'Rodger Young', }, { name:'Hoban Washburne', ship:'Serenity' }, { name:'James Kirk', ship:'USS Enterprise' } ]; var result =_.values((_.groupBy(myObjArray , 'ship'))); console.log(result); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.16.4/lodash.min.js"></script> 

Probably not the most performant but this should work.

var tempObj = {};
myObjArray.forEach((item)=>{
  var ship = item.ship;
  if (!tempObj.hasOwnProperty(ship)) {
    tempObj[ship] = []; //create the key in the key in the obj and init to an empty array
  }
  tempObj[ship].push(item); //add the item to the array
});

var myNewObjArray = [];

for (key in tempObj) {
  myNewObjArray.push([]); //add a new array for each key in the tempObj
  tempObj[key].forEach((item)=>{ //iterate over the array of items in the tempObj for that key
    myNewObjArray[myNewObjArray.length-1].push(item); //add the item to the last array in the object which should have been created.
  });
}

Not sure how you plan on using the data but would a more concise data structure look something like an object where the ship has staff rather than an array of arrays where the ship name is continually repeated in a redundant way? What about this data structure?

 var myObjArray = [ { name:'Malcolm Reynolds', ship:'Serenity' }, { name: 'Carmen Ibanez', ship: 'Rodger Young', }, { name: 'Zander Barcalow', ship: 'Rodger Young', }, { name:'Hoban Washburne', ship:'Serenity' }, { name:'James Kirk', ship:'USS Enterprise' } ]; const staffShips = data => data.reduce((ships, item) => { const ship = ships[item.ship]; if (ship) { ship.push(item.name); } else { ships[item.ship] = [ item.name ]; } return ships; }, {}); console.log(staffShips(myObjArray)); 

This is slightly different in that it's an object with keys, but that those keys contain arrays with the data how you want to see it.

var newObject = {};

for (var i in myObjArray) {
     var newKey = myObjArray[i].ship.replace(/\s+/g, '');
   if (typeof(newObject[newKey]) == "undefined") newObject[newKey] = [];
   newObject[newKey].push({
        name: myObjArray[i].name, ship: myObjArray[i].ship
   });
}

Here you have another approach, first, we use Array.reduce() to generate an object that will group elements by the ship property. Then we use Array.map() over the generated Object.values() to drop the array if only holds one element. The map could be optional if you don't really need this last step.

 var myObjArray = [ {name:'Malcolm Reynolds', ship:'Serenity'}, {name: 'Carmen Ibanez', ship: 'Rodger Young'}, {name: 'Zander Barcalow', ship: 'Rodger Young'}, {name:'Hoban Washburne', ship:'Serenity'}, {name:'James Kirk', ship:'USS Enterprise'} ]; let res = myObjArray.reduce((acc, obj) => { acc[obj.ship] = acc[obj.ship] || []; acc[obj.ship].push(obj); return acc; }, {}); res = Object.values(res).map(arr => (arr.length <= 1 ? arr[0] : arr)); console.log(res); 
 .as-console {background-color:black !important; color:lime;} .as-console-wrapper {max-height:100% !important; top:0;} 

In Javascript, to group objects by property, Array.prototype.reduce() method can be used to consolidate the input array data into a set of results, grouped by a key( in this case 'ship'). Use Object.values to extract the values from the resulting set by dropping the keys

 var data = [ { name: 'Malcolm Reynolds', ship: 'Serenity' }, { name: 'Carmen Ibanez', ship: 'Rodger Young' }, { name: 'Zander Barcalow', ship: 'Rodger Young' }, { name: 'Hoban Washburne', ship: 'Serenity' }, { name: 'James Kirk', ship: 'USS Enterprise' }]; var myNewObjArray = data.reduce((res,obj) =>{ const key = obj.ship; if(!res[key]){ res[key] = []; } res[key].push(obj) return res; }, {}); console.log(Object.values(myNewObjArray)); 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM