2

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.

  • 1
    please add your attempt. why is the last item not wrapped in an array? – Nina Scholz Apr 18 at 20:42
  • use .sort(customCompare) where customCompare = function(a,b){.....} – Alex Kudryashev Apr 18 at 20:46
4

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

  • Thanks Nina, I like the use of reduce I didn't consider that. – Ian Gray Apr 18 at 20:56
  • Since it's the accepted answer, I'll vote up too. – Harry Chilinguerian Apr 18 at 21:04
  • This is the accepted answer because my data was much more complex than the example and I was able to quickly modify this one to my needs, which I imagine others will as well (just had to add an additional map to match another value if item.ship if it didn't exist). – Ian Gray Apr 18 at 21:21
2

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

  • Super simple and clean thank you... my version involved 3 arrays I was trying to match together original, one with just ships, then a new one based on the indexes of just ships... – Ian Gray Apr 18 at 20:54
1

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>

0

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.
  });
}
  • 1
    Thanks Harry, not far off from what I slapped together but a bit cleaner. – Ian Gray Apr 18 at 20:56
0

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

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

0

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

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.