-1
\$\begingroup\$

Is there a better way to do this?

I have a list of objects.

I want the list has two values: an ID and a NAME.

Both Name & ID are distinct taken separately of together.

{ 'Nam': 'Joe', 'ID' : 1 }
{ 'Nam': 'Mary', 'ID' : 2 }
{ 'Nam': 'Joe', 'ID' : 1 }
{ 'Nam': 'Mary', 'ID' : 2 }
{ 'Nam': 'Biggie', 'ID' : 3 }
{ 'Nam': 'Joe', 'ID' : 1 }

Joe is always 1, Mary 2, Biggie 3, ...

Then end result is to just be:

{ 'Nam': 'Mary', 'ID' : 2 }
{ 'Nam': 'Biggie', 'ID' : 3 }
{ 'Nam': 'Joe', 'ID' : 1 }

.

var rRec = new Array();
rec.reduce(function(a, i) {
    if (a.indexOf(i['Nam']) < 0) { 
        a.push(i['Nam']); 
        rRec.push({ 'Nam' : i['Nam'], 'Id' : i['Id'] }); 
    }
    return a;
}, []);

console.log('rRec', rRec);
\$\endgroup\$
  • \$\begingroup\$ So you basically just want to squash the duplicates? \$\endgroup\$ – Alexander Sep 18 '17 at 20:08
  • \$\begingroup\$ And return an list of object that contains two attributes ~ where (not demonstrated) the original object contains 7 attributes. \$\endgroup\$ – user5903880 Sep 18 '17 at 20:49
  • \$\begingroup\$ so first map your complex objects (those with 7 fields) to simple ones (those with 2), and then just squash the duplicates in the simple ones \$\endgroup\$ – Alexander Sep 18 '17 at 21:00
1
\$\begingroup\$

A different approach would be to add the objects to the cumulative array, instead of the ID, then check to see if there exists an object with that ID. Then there is no need for an extra array just to hold the IDs.

var rec = [{ 'Nam': 'Joe', 'ID' : 1 },
{ 'Nam': 'Mary', 'ID' : 2 },
{ 'Nam': 'Joe', 'ID' : 1 },
{ 'Nam': 'Mary', 'ID' : 2 },
{ 'Nam': 'Biggie', 'ID' : 3 },
{ 'Nam': 'Joe', 'ID' : 1 }];
var output = rec.reduce(function(a, i) {
    if (a.findIndex(function(element) { return i.ID == element.ID; }) < 0) { 
        a.push(i); 
    }
    return a;
}, []);
console.log('output: ',output);

Edit:

Well, fiddlesticks - it appears that approach isn't faster... try this performance comparison test... I tried using some() instead of findIndex() but that still was slower than the original code.

if (!a.some(function(element) { return i.ID == element.ID; })) { 
    a.push(i); 
}

I also tried using an object instead of an array but that still was slower than the original code.

var output = Object.values(rec.reduce(function(a, i) {
    if (!a[i.ID]) { 
        a[i.ID] = i;
    }
    return a;
}, {}));

I was able to get a faster (in Firefox that is - maybe not in Chrome or Opera) approach using forEach() and findIndex(). Here is the performance comparison tests.

var rec = [{ 'Nam': 'Joe', 'ID' : 1 },
    { 'Nam': 'Mary', 'ID' : 2 },
    { 'Nam': 'Joe', 'ID' : 1 },
    { 'Nam': 'Mary', 'ID' : 2 },
    { 'Nam': 'Biggie', 'ID' : 3 },
    { 'Nam': 'Joe', 'ID' : 1 }];
var rRec = new Array();
rec.forEach(function(i) {
    if (rRec.findIndex(function(item) { 
        return item.ID == i.ID;}) < 0) { 
        rRec.push(i);
    }
});
\$\endgroup\$
  • \$\begingroup\$ But my actual object has 5 additional attributes. Values for these will vary across instances of 'Joe'. 'Mary', ..., I guess since I don't really care that the additional data is included in the var output given your code. I'll never need to reference these additional attributes. \$\endgroup\$ – user5903880 Sep 18 '17 at 20:00
1
\$\begingroup\$

Javascript's Map object

You can use a Map to reduce the array of objects. A Map holds a list of key, value pairs. Each Key is unique and can be any Javascript Object or primitive type, the same for the associated value.

You could create a generic function to handle many data types. The unique index needs to be defined for the function and could be handled as a callback the takes as an argument an array item and returns a string containing the index.

Also I assume that the name is not unique and that is why you need an ID as well. I added two names each with and different ID just to test.

// test data
const data = [
    { Name: 'Joe',      ID : 1 },
    { Name: 'Mary',     ID : 2 },
    { Name: 'Joe',      ID : 1 },
    { Name: 'Mary',     ID : 2 },
    { Name: 'Biggie',   ID : 3 },
    { Name: 'Joe',      ID : 1 },
    { Name: 'Mark',     ID : 4 }, // Added same name with different IDs
    { Name: 'Mark',     ID : 5 },
];

// data the array to reduce
// indexFunc Returns a index for a given item eg (item) => item.name
// Function returns a new array contining only unique items.
function reduceArray (data, indexFunc) {
    const reducedData = new Map();
    data.forEach(item => reducedData.set(indexFunc(item), item) );
    return [...reducedData.values()]; // convert the Map back to an array of items.
}

// Reduce the data array
const dataR = reduceArray(data, item => item.Name + item.ID);

// Show result.
dataR.forEach(item => console.log(item.Name + " : " + item.ID) );

\$\endgroup\$

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.