9

I have an array of javascript objects that are products. These products are displayed in a list as a cart.

I want to count the number of duplicate products in the array based in the _.id value and remove these objects from the array and relace them with an updated version and a new key called count with a value of the total number of times this object comes up.

So far, I have tried numerous methods and I've searched all over google but there's nothing that I've found that can do the job correctly.

An example of the type of array that I will be using would be this:

[

    {  _id: "5971df93bfef201237985c4d", 
       slug: "5971df93bfef201237985c4d", 
       taxPercentage: 23, 
       totalCost: 9.99, 
       currency: "EUR", 
    },

]

so what I would want my end result to be would be something like this - it removes the duplicate value and replaces it with the same object but adds a new key called count with a value of the number of times the object initially was in the array:

[

    {  _id: "5971df93bfef201237985c4d", 
       slug: "5971df93bfef201237985c4d", 
       taxPercentage: 23, 
       totalCost: 9.99, 
       currency: "EUR", 
       count: 2, // whatever the count is
    },

]

So far I'm using this method:

var count = [];

if (cart.cart.products != undefined) {
    let namestUi = {
        renderNames(names){
            return Array.from(
                names.reduce( (counters, object) =>
                        counters.set(object._id, (counters.get(object._id) || 0) + 1),
                    new Map() ),
                ([object, count]) => {
                    var filterObj = names.filter(function(e) {
                        return e._id == object;
                    });

                    return ({filterObj, count})
                }
            );
        }
    };

    count = namestUi.renderNames(cart.cart.products);
    console.log(count)
}

but it returns the values like this:

{filterObj: Array // the array of the duplicates, count: 2}
{filterObj: Array, count: 1}

and since I am using React-Native with a list view something like this won't work.

It just needs to store the items the way it was before (an array) but with a new child called count.

Any help is welcomed!

2
  • You have an array of object, in which object of same id are occurring multiple times. Now you want to create an array which combines same objects into one and adds a count attribute ( indicating how many times same object has come) . Is this correct ?? Commented Jul 22, 2017 at 19:54
  • Yes that's pretty much it Commented Jul 22, 2017 at 20:08

3 Answers 3

21

I would stick to reduce, use a Map and spread its values to get the final result:

const names = [{  _id: 1 }, { _id: 1}, { _id: 2}, { _id: 1}];

const result = [...names.reduce( (mp, o) => {
    if (!mp.has(o._id)) mp.set(o._id, { ...o, count: 0 });
    mp.get(o._id).count++;
    return mp;
}, new Map).values()];

console.log(result);

Or you can first create all the keys in the map with a zero count (using the Map constructor), and then iterate the data again to update the counter. This split of tasks makes the code more concise than with reduce:

const names = [{  _id: 1 }, { _id: 1}, { _id: 2}, { _id: 1}];

const mp = new Map(names.map(o => [o._id, {...o, count: 0 }]));
for (const {_id} of names) mp.get(_id).count++;
const result = Array.from(mp.values());

console.log(result);

When you would have more than one key, then one idea is to join those with JSON.stringify([ ]):

const names = [{cat: 1, sub: 1}, {cat: 1, sub: 2}, {cat: 2, sub: 1}, {cat: 1, sub: 1}];

const result = [...names.reduce( (mp, o) => {
    const key = JSON.stringify([o.cat, o.sub]);
    if (!mp.has(key)) mp.set(key, { ...o, count: 0 });
    mp.get(key).count++;
    return mp;
}, new Map).values()];

console.log(result);

Sign up to request clarification or add additional context in comments.

1 Comment

This worked out great too, alot cleaner and nicer sytax
5

The easiest would be probably a map:

var map=new Map();

names.forEach(function(el){
 if(map.has(el["_id"])){
  map.get(el["_id"]).count++;
 }else{
  map.set(el["_id"],Object.assign(el,{count:1}));
 }
});  

And then recreate an array:

names=[...map.values()];

Or in old hash/array way:

var hash={},result=[];

names.forEach(function(name){
  var id=name["_id"];
  if(hash[id]){
     hash[id].count++;
  }else{
     result.push(hash[id]={
        count:1,
        ...name
     });
  }
});

console.log(result);

2 Comments

With Object.assign(el,{count:1}) you modify the original object. You should do the inverse: Object.assign({count:1}, el), or, to deal with and overwrite an existing count property: Object.assign({}, el, {count:1})
@trincot yeah provided two solutions, i thought OP could choose a mixin if he cares about performance or unchanged original.
3

You can make use of array.reduce method to convert an original array into a new array with desired structure. We can just check if the id exists in the array and accordingly update the array with new object having count property.

let arr = [{
  id: 1
}, {
  id: 1
}, {
  id: 1
}, {
  id: 2
}, {
  id: 2
}];

let new_arr = arr.reduce((ar, obj) => {
  let bool = false;
  if (!ar) {
    ar = [];
  }
  ar.forEach((a) => {
    if (a.id === obj.id) {
      a.count++;
      bool = true;
    }
  });
  if (!bool) {
    obj.count = 1;
    ar.push(obj);
  }
  return ar;
}, []);

console.log(new_arr);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.