ronoc4
ronoc4

Reputation: 637

Arrange a duplicate object in a Javascript array to produce one value

I'm trying to arrange my current object, which looks like below

var myValues = [
{
    client: "FIRE",
    firstname: "Test",
    id: "[email protected]",
    arrangeid: "FIREFOX",
    region: "FOP",
    secondname: "Testy",
    status: "Approved"
},
{
    client: "FIRE",
    firstname: "Test",
    id: "[email protected]",
    arrangeid: "BUZZZZZZ",
    region: "FOP",
    secondname: "Testy",
    status: "Approved"
},
{
    client: "PANTER",
    firstname: "Panty",
    id: "[email protected]",
    arrangeid: "PANTER",
    region: "PAN",
    secondname: "mc panty",
    status: "Approved"
},
{
    client: "BAT",
    firstname: "Bruce",
    id: "[email protected]",
    arrangeid: "BLACKBAT",
    region: "BLK",
    secondname: "Wyne",
    status: "Approved"
}

]

So I've got this object, the first two ID's are the same but they got different arrangeid. I wanna be able to join the them to gather them together. Where the object looks something like this:

    var myValues = [
{
    client: "FIRE",
    firstname: "Test",
    id: "[email protected]",
    arrangeid: ["BUZZZZZZ", "FIREFOX"],
    region: "FOP",
    secondname: "Testy",
    status: "Approved"
},
{
    client: "PANTER",
    firstname: "Panty",
    id: "[email protected]",
    arrangeid: ["PANTER"],
    region: "PAN",
    secondname: "mc panty",
    status: "Approved"
},
{
    client: "BAT",
    firstname: "Bruce",
    id: "[email protected]",
    arrangeid: ["BLACKBAT"],
    region: "BLK",
    secondname: "Wyne",
    status: "Approved"
}

]

I have a jsfidle here, where i kinda get a result. Can it be done better, cleaner. Have I approached it correctly?

Upvotes: 0

Views: 50

Answers (5)

Scott Sauyet
Scott Sauyet

Reputation: 50797

Here is a slightly more generic approach, which lets you group by any combination of fields and then merge some given ones into an array:

const pick = (fields, obj) => fields.reduce((o, fld) => ({...o, [fld]: obj[fld]}), {})

const groupAndMerge = (groupFields, mergeFields, xs) => 
  Object.values(xs.reduce(
    (a, x, _, __, key = JSON.stringify(pick(groupFields, x))) => 
      (a[key] = (a[key] || []).concat(x)) && a,
    {}
  )).map(xs => ({
    ...xs[0],
    ...mergeFields.reduce((a, f) => ({...a, [f]: xs.map(x => x[f])}), {})
  }))

const myValues = [{arrangeid: "FIREFOX", client: "FIRE", firstname: "Test", fid: "[email protected]", region: "FOP", secondname: "Testy", status: "Approved"}, {arrangeid: "BUZZZZZZ", client: "FIRE", firstname: "Test", fid: "[email protected]", region: "FOP", secondname: "Testy", status: "Approved"}, {arrangeid: "PANTER", client: "PANTER", firstname: "Panty", fid: "[email protected]", region: "PAN", secondname: "mc panty", status: "Approved"}, {arrangeid: "BLACKBAT", client: "BAT", firstname: "Bruce", fid: "[email protected]", region: "BLK", secondname: "Wyne", status: "Approved"}]

const result = groupAndMerge(['client', 'id'], ['arrangeid'], myValues)

console.log(result)

The helper function pick simply chooses certain properties of the target object, so that pick(['a', 'c'], {a: 1, b: 2, c: 3}) //=> {a: 1, c: 3}.

groupAndMerge uses the first parameter (here ['client', 'id']) to identify the fields which have to be the same, and the second one (here ['arrangeid']) to show which ones have to be grouped.

You mention that the first two have the same id. My code checks both client and id; this is as much as anything a proof-of-concept, but it could be useful.

There is one caveat to using this: it only merges well on string or numeric values. Some others might work, but I wouldn't count on it. (That is also true for at least one other answer here, but I don't know if it's important to you.)

The advantage I see to this is that it's only slightly more complex than a bespoke implementation, and it's useful in a number of other circumstances.

Upvotes: 0

jfriend00
jfriend00

Reputation: 707546

For larger data sets, you can use the lookup capabilities of a Map object to collect all the items for each id and then you can convert that result into whatever format you want. You can run this snippet to see the result.

function collectByField(data, key, prop) {
    const collection = new Map();
    for (const obj of data) {
         let item = collection.get(obj[key]);
         if (item) {
             // add this item's property to the array
             item[prop].push(obj[prop]);
         } else {
             // put a copy of our object into the Map
             // convert prop to an array with one initial item in it
             let copy = Object.assign({}, obj);
             copy[prop] = [obj[prop]];
             collection.set(obj[key], copy);
         }
    }
    // now use the Set to create final array output
    return Array.from(collection.values());
}

let myValues = [
{
    client: "FIRE",
    firstname: "Test",
    id: "[email protected]",
    arrangeid: "FIREFOX",
    region: "FOP",
    secondname: "Testy",
    status: "Approved"
},
{
    client: "FIRE",
    firstname: "Test",
    id: "[email protected]",
    arrangeid: "BUZZZZZZ",
    region: "FOP",
    secondname: "Testy",
    status: "Approved"
},
{
    client: "PANTER",
    firstname: "Panty",
    id: "[email protected]",
    arrangeid: "PANTER",
    region: "PAN",
    secondname: "mc panty",
    status: "Approved"
},
{
    client: "BAT",
    firstname: "Bruce",
    id: "[email protected]",
    arrangeid: "BLACKBAT",
    region: "BLK",
    secondname: "Wyne",
    status: "Approved"
}
];

console.log(collectByField(myValues, "id", "arrangeid"));

Upvotes: 0

trincot
trincot

Reputation: 350365

You could create a Map to key the objects by their id, and then populate the arrangeid arrays:

const myValues = [{client: "FIRE",firstname: "Test",id: "[email protected]",arrangeid: "FIREFOX",region: "FOP",secondname: "Testy",status: "Approved"},{client: "FIRE",firstname: "Test",id: "[email protected]",arrangeid: "BUZZZZZZ",region: "FOP",secondname: "Testy",status: "Approved"}, {client: "PANTER",firstname: "Panty",id: "[email protected]",arrangeid: "PANTER",region: "PAN",secondname: "mc panty",status: "Approved"},{client: "BAT",firstname: "Bruce",id: "[email protected]",arrangeid: "BLACKBAT",region: "BLK",secondname: "Wyne",status: "Approved"}];

const map = new Map(myValues.map(o => [o.id, {...o, arrangeid: []}]));
myValues.forEach(o => map.get(o.id).arrangeid.push(o.arrangeid));
const result = [...map.values()];

console.log(result);

Upvotes: 0

adiga
adiga

Reputation: 35242

You can use reduce, Object.values() and spread syntax like this:

var myValues = [{client:"FIRE",firstname:"Test",id:"[email protected]",arrangeid:"FIREFOX",region:"FOP",secondname:"Testy",status:"Approved"},{client:"FIRE",firstname:"Test",id:"[email protected]",arrangeid:"BUZZZZZZ",region:"FOP",secondname:"Testy",status:"Approved"},{client:"PANTER",firstname:"Panty",id:"[email protected]",arrangeid:"PANTER",region:"PAN",secondname:"mc panty",status:"Approved"},{client:"BAT",firstname:"Bruce",id:"[email protected]",arrangeid:"BLACKBAT",region:"BLK",secondname:"Wyne",status:"Approved"}];

const merged = myValues.reduce((acc, a) => {
  acc[a.client] = acc[a.client] || { ...a, arrangeid: [] };
  acc[a.client].arrangeid.push(a.arrangeid);
  return acc;
},{})

const output = Object.values(merged);
console.log(output)

The accumulator is an object with each unique client as its key so that it's easier to group them.

{
  "FIRE": {
    "client": "FIRE",
    "firstname": "Test",
    "id": "[email protected]",
    "arrangeid": [ "FIREFOX", "BUZZZZZZ" ],
    "region": "FOP",
    "secondname": "Testy",
    "status": "Approved"
  },
  "PANTER": {
    "client": "PANTER",
    "id": "[email protected]",
    "arrangeid": [
      "PANTER"
    ],
   ...
  }
  ...
}

Upvotes: 1

Maheer Ali
Maheer Ali

Reputation: 36584

You can use reduce()

var myValues = [
{
    client: "FIRE",
    firstname: "Test",
    id: "[email protected]",
    arrangeid: "FIREFOX",
    region: "FOP",
    secondname: "Testy",
    status: "Approved"
},
{
    client: "FIRE",
    firstname: "Test",
    id: "[email protected]",
    arrangeid: "BUZZZZZZ",
    region: "FOP",
    secondname: "Testy",
    status: "Approved"
},
{
    client: "PANTER",
    firstname: "Panty",
    id: "[email protected]",
    arrangeid: "PANTER",
    region: "PAN",
    secondname: "mc panty",
    status: "Approved"
},
{
    client: "BAT",
    firstname: "Bruce",
    id: "[email protected]",
    arrangeid: "BLACKBAT",
    region: "BLK",
    secondname: "Wyne",
    status: "Approved"
}

]



let res = JSON.parse(JSON.stringify(myValues)).reduce((ac,a) => {
  let ind = ac.findIndex(x => x.id === a.id);
  a.arrangeid = [a.arrangeid];
  ind === -1 ? ac.push(a) : ac[ind].arrangeid.push(...a.arrangeid);
  return ac;
},[])
console.log(res);

Upvotes: 0

Related Questions