2

I have an array of objects where each object has a an id key. some of those objects have re-occuring id's and I want to remove those re-occuring objects.

So for instance:

let array = [{
    "id": "123",
    "country": "Brazil",
    "address": "xyz abc",
    "date": "Dec 17, 1995, 9:45:17 PM"
  },
  {
    "id": "443",
    "country": "Russia",
    "address": "qwd qwd qwdqw",
    "date": "Dec 17, 1965, 9:45:17 PM"
  },
  {
    "id": "123",
    "country": "Canada",
    "address": "ktktkt",
    "date": "Dec 17, 1925, 9:45:17 PM"
  },
.
.
.
{}]

in the array above since index 0 and index 2 share the same id key value, I would like to completely remove them both from the array.

  • I am looking for optimal code in terms of complexity, only linear (O(n)).
5
  • 2
    This isn't a use case for reduce. It might be a use case for filter, but really, a simple loop keeping track of objects you've seen for a given ID before temporarily in a Map (or an object) is all you need. Your best bet here is to do your research, search for related topics on SO, and give it a go. If you get stuck and can't get unstuck after doing more research and searching, post a minimal reproducible example of your attempt and say specifically where you're stuck. People will be glad to help. Commented Feb 27, 2019 at 8:35
  • There are dozens of questions with answers on SO about how to do that. Commented Feb 27, 2019 at 8:37
  • 2
  • 1
    Am I missing something or you made a typo? Linear complexity is represented by O(n) not log(O) (I don't even know how to read this nor what it is). Commented Feb 27, 2019 at 9:07
  • Bardr - you are correct and some gentleman (or woman! lol) gallantly edited it for me, thanks! Commented Feb 27, 2019 at 9:16

8 Answers 8

1

Since you want to remove completely the repeated values you can try this. First find the repetitions and then filter the original array.

let array = [{
    "id": "123",
    "country": "Brazil"
  },{
    "id": "443",
    "country": "Russia"
  },{
    "id": "123",
    "country": "Canada"
  },{
    "id": "123",
    "country": "Canada"
  },{
    "id": "345",
    "country": "UK"
  }];

const removeDups = (data) => {
	
	const dups = data.reduce((acc, { id }) => {
		acc[id] = (acc[id] || 0) + 1;
		return acc;
	}, {});

	return data.filter(({ id }) => dups[id] === 1);
}

console.log(removeDups(array));

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

4 Comments

I really like this function that segregates the duplicate counting and then goes in to filter. What's the complexity of removeDups() ?
@clusterBuddy Thank you, I believe it's O(n).
Thanks Alex. i'm learning how to use reduce, can I use in this function a counter to count the reoccurance of country names in reducer?
@clusterBuddy Yes you could use the country prop instead of the id. So you would end up with an object, countries as keys and the reoccurances as values.
1

I don't know, maybe this?:

array.filter(function(d,i){return !this[d.id] && (this[d.id] = d.id)},{})

Comments

1

No need for reduce, just sort and filter:

let array = [{
    "id": "123",
    "country": "Brazil",
    "address": "xyz abc",
    "date": "Dec 17, 1995, 9:45:17 PM"
  },
  {
    "id": "443",
    "country": "Russia",
    "address": "qwd qwd qwdqw",
    "date": "Dec 17, 1965, 9:45:17 PM"
  },
  {
    "id": "123",
    "country": "Canada",
    "address": "ktktkt",
    "date": "Dec 17, 1925, 9:45:17 PM"
  },
  
]


const output = array.sort((a, b) => a.id - b.id).filter((item, index, sorted) => {
  const before = sorted[index - 1] || {}
  const after = sorted[index + 1] || {}

  return item.id !== after.id && item.id !== before.id
})

console.log(output);

Comments

0

You don't need reduce at all - a simple for loop does the job:

let array = [{
    "id": "123",
    "country": "Brazil",
    "address": "xyz abc",
    "date": "Dec 17, 1995, 9:45:17 PM"
  },
  {
    "id": "443",
    "country": "Russia",
    "address": "qwd qwd qwdqw",
    "date": "Dec 17, 1965, 9:45:17 PM"
  },
  {
    "id": "123",
    "country": "Canada",
    "address": "ktktkt",
    "date": "Dec 17, 1925, 9:45:17 PM"
  }
]

array.forEach(i => {
  let found = false
  array.forEach(j => {
    if (j == i) {
      found++;
    }
  });
  if (found) {
    array.forEach((k, l) => {
      if (k == i) {
        array.splice(l, 1);
        l--;
      }
    });
  }
});

console.log(array);

2 Comments

hey Jack, thx for reply, please note that your solution an exponential complexity.
Sorry @clusterBuddy I'm not that good with algorithms conforming to a complexity.
0

You could take a Map and if exist, set the mapeed array to the length of zero. At the end concat all arrays.

var array = [{ id: "123", country: "Brazil", address: "xyz abc", date: "Dec 17, 1995, 9:45:17 PM" }, { id: "443", country: "Russia", address: "qwd qwd qwdqw", date: "Dec 17, 1965, 9:45:17 PM" }, { id: "123", country: "Canada", address: "ktktkt", date: "Dec 17, 1925, 9:45:17 PM" }],
    result = [].concat(...array.map((m => (o, i) => {
        var temp = [];
        if (m.has(o.id)) {
            m.get(o.id).length = 0;
        } else {
            m.set(o.id, temp = [o]);
        }
        return temp;
   })(new Map)))

console.log(result);

Comments

0

You can simply initialize an count object and populate it using a simple forEach loop then just use filter.

let arr = [{ id: "123", country: "Brazil", address: "xyz abc", date: "Dec 17, 1995, 9:45:17 PM" }, { id: "443", country: "Russia", address: "qwd qwd qwdqw", date: "Dec 17, 1965, 9:45:17 PM" }, { id: "123", country: "Canada", address: "ktktkt", date: "Dec 17, 1925, 9:45:17 PM" }]

count = {}

arr.forEach(obj => {
  if (count[obj.id]) {
      count[obj.id] += 1
  } else {
      count[obj.id] = 1
  } 
})



console.log(arr.filter(obj => count[obj.id] === 1))

Run time (code-complexity): O(N)

Comments

0

One solution would be to aggregate the input array to a key/value map, where the value is a list of items sharing the same id. You'd then extract an array from this map via Object.values(), filter values with more than one item, and then map those single items to the final output:

let array = [{
    "id": "123",
    "country": "Brazil",
    "address": "xyz abc",
    "date": "Dec 17, 1995, 9:45:17 PM"
  },
  {
    "id": "443",
    "country": "Russia",
    "address": "qwd qwd qwdqw",
    "date": "Dec 17, 1965, 9:45:17 PM"
  },
  {
    "id": "123",
    "country": "Canada",
    "address": "ktktkt",
    "date": "Dec 17, 1925, 9:45:17 PM"
  }
]

const result = Object.values(array.reduce((map, item) => {

    map[item.id] = (map[item.id] || []).concat([item]);

    return map;

  }, {}))
  .filter(item => item.length === 1)
  .map(([item]) => item)

console.log(result)

Comments

0

Use Array.reduce() to build an intermediate dictionary by id whose values are all items with that id. Then enumerate the values of this dictionary with Object.values() and filter out the entries with multiple elements using Array.filter(), then flatten the result using Array.flat():

const array = [{
    "id": "123",
    "country": "Brazil",
    "address": "xyz abc",
    "date": "Dec 17, 1995, 9:45:17 PM"
  },
  {
    "id": "443",
    "country": "Russia",
    "address": "qwd qwd qwdqw",
    "date": "Dec 17, 1965, 9:45:17 PM"
  },
  {
    "id": "123",
    "country": "Canada",
    "address": "ktktkt",
    "date": "Dec 17, 1925, 9:45:17 PM"
  },
];

const singles = Object.values(array.reduce((acc, x) => {
  acc[x.id] = [...(acc[x.id] || []), x];
  return acc;
}, {})).filter(x => x.length === 1).flat();

console.log(singles);

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.