0

I have an array of objects.

const arr = [
  { title: "sky", artist: "Jon", id: 1 },
  { title: "rain", artist: "Paul", id: 2 },
  { title: "sky", artist: "Jon", id: 1 }
];

I would like to remove all duplicates from the array based on id. The final result should be

[{ title: "rain", artist: "Paul", id: 2 }]

If the array is

const arr = [
  { title: "sky", artist: "Jon", id: 1  },
  { title: "rain", artist: "Paul", id: 2  },
  { title: "sky", artist: "Jon", id: 1  },
  { title: "rain", artist: "Paul", id: 2  },
];

The result should be an [].

This is what I tried:

const arr = [{
    title: "sky",
    artist: "Jon",
    id: 1
  },
  {
    title: "rain",
    artist: "Paul",
    id: 2
  },
  {
    title: "sky",
    artist: "Jon",
    id: 1
  }
];
const uniqueScenarios = Array.from(new Set(arr.map(a => a.id)))
  .map(id => {
    return arr.find(a => a.id === id)
  })

console.log(uniqueScenarios)

const arr1 = [{
    title: "sky",
    artist: "Jon",
    id: 1
  },
  {
    title: "rain",
    artist: "Paul",
    id: 2
  },
  {
    title: "sky",
    artist: "Jon",
    id: 1
  },
  {
    title: "rain",
    artist: "Paul",
    id: 2
  }
];
const uniqueScenarios1 = Array.from(new Set(arr1.map(a => a.id)))
  .map(id => {
    return arr1.find(a => a.id === id)
  })

console.log(uniqueScenarios1)

Please advice. I am open to lodash solutions as well. This is the final solution I am expecting. I am able to add Stackblitz link

arunmmanoharan
  • 2,535
  • 2
  • 29
  • 60

8 Answers8

2

You could take an object and filter with the value of the hash table.

const
    array = [{ title: "sky", artist: "Jon", id: 1 }, { title: "rain", artist: "Paul", id: 2 }, { title: "sky", artist: "Jon", id: 1 }, { title: "rain", artist: "Paul", id: 2 }],
    ids = array.reduce((r, { id }) => (r[id] = !(id in r), r), {}),
    result = array.filter(({ id }) => ids[id]);

console.log(result);
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
1

That's a one-liner:

list.filter(el => list.filter(e => e.title == el.title).length == 1);

const arr = [{
    title: "sky",
    artist: "Jon",
    id: 1
  },
  {
    title: "rain",
    artist: "Paul",
    id: 2
  },
  {
    title: "sky",
    artist: "Jon",
    id: 1
  }
];

const arr1 = [{
    title: "sky",
    artist: "Jon",
    id: 1
  },
  {
    title: "rain",
    artist: "Paul",
    id: 2
  },
  {
    title: "sky",
    artist: "Jon",
    id: 1
  },
  {
    title: "rain",
    artist: "Paul",
    id: 2
  }
];


function removeDupes(list) {
  return list.filter(el => list.filter(e => e.id == el.id).length == 1);
}

console.log(removeDupes(arr));
console.log(removeDupes(arr1));
Balastrong
  • 4,336
  • 2
  • 12
  • 31
  • Could you help me out here as well? https://stackblitz.com/edit/react-ts-a9pgna?file=ValidateFile.tsx I am able to add items but unable to remove them – arunmmanoharan Jun 16 '20 at 20:50
0

One way to do it, in order to avoid running an exponential loop is to save all values to a new object, and convert that object to a new array.

const combinedObj = arr.reduce((obj, item) => { obj[item.id] = item; return obj; }, {});
const newArray = Object.values(combinedObj)
  • This is not what OP is asking. They want to remove all the objects of an `id` if an `id` appears more than once. – adiga Jun 16 '20 at 17:43
  • exactly. You'll keep adding it to a reducer object by a key name: so reducer[1] = {id: 1}, overwriting previous value every time and end up with an object containing list of unique values; – Dmitriy Godlevski Jun 16 '20 at 17:47
0

you can do it in one line like this:

const res = arr.filter(elem => (arr.filter(obj => obj.id === elem.id)).length === 1)

or you can do it like this(better in terms of time complexity):

const arr = [
  { title: "sky", artist: "Jon", id: 1  },
  { title: "rain", artist: "Paul", id: 2  },
  { title: "sky", artist: "Jon", id: 1  },

];

const counts = arr.reduce((counts, curr) => (counts[curr.id] = ++counts[curr.id] || 1, counts), {})
const res = arr.filter(curr => counts[curr.id] === 1)


0

You could run the array through reduce() and then use some to see if you should add if it does not exist, or remove using filter if it does.

Snippet:

const arr = [
  { title: "sky", artist: "Jon", id: 1  },
  { title: "rain", artist: "Paul", id: 2  },
  { title: "sky", artist: "Jon", id: 1  },
  { title: "rain", artist: "Paul", id: 2  },
  { title: "earth", artist: "Frank", id: 3  },
];

const unique = arr.reduce((accumulator, currentValue) => { 
  // add if we don't have
  if (!accumulator.some(x => x.id === currentValue.id)) {
    accumulator.push(currentValue);
  } else {
    // remove if we do
    accumulator = accumulator.filter(x => x.id !== currentValue.id);
  }
  
  return accumulator;
}, []); 

console.info(unique);
Samuel Goldenbaum
  • 18,391
  • 17
  • 66
  • 104
0

you can sort the data by id,then each item that has different id compared with next element and prev element should be added to the result array.

const arr = [
  { title: 'sky', artist: 'Jon', id: 1 },
  { title: 'rain', artist: 'Paul', id: 2 },
  { title: 'sky', artist: 'Jon', id: 1 },
  { title: 'sky', artist: 'Jon', id: 1 },
  { title: 'rain', artist: 'Paul', id: 2 },
  { title: 'test', artist: 'test', id: 3 },
]

arr.sort((a, b) => a.id - b.id)
var res = []
arr.forEach((item, index) => {
  if (index < arr.length - 1 && arr[index + 1]) {
    if (item.id === arr[index + 1].id) {
      return
    }
  }
  if (index > 0 && arr[index - 1]) {
    if (item.id === arr[index - 1].id) {
      return
    }
  }
  res.push(item)
})

console.log(res)

output

0: {title: "test", artist: "test", id: 3}
Ehsan Nazeri
  • 771
  • 1
  • 6
  • 10
0

You can group the items by id, and then use _.flatMap() to convert back a single array. In the _.flatMap() callback return an empty array if the group has more than one item:

const fn = arr => _.flatMap(
  _.groupBy(arr, 'id'), // group by the id
  group => _.size(group) > 1 ? [] : group // check the size and return an empty array for groups with more than a single item
)

const arr1 = [{"title":"sky","artist":"Jon","id":1},{"title":"rain","artist":"Paul","id":2},{"title":"sky","artist":"Jon","id":1}]
const arr2 = [{"title":"sky","artist":"Jon","id":1},{"title":"rain","artist":"Paul","id":2},{"title":"sky","artist":"Jon","id":1},{"title":"rain","artist":"Paul","id":2}]

console.log(fn(arr1))
console.log(fn(arr2))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>

Another example

Ori Drori
  • 183,571
  • 29
  • 224
  • 209
0

If you know the title, artist, id are gonna be in the same order in each of the object, one solution can be this:

var arrayX=[
  { title: "sky", artist: "Jon", id: 1 },
  { title: "rain", artist: "Paul", id: 2 },
  { title: "sky", artist: "Jon", id: 1 }
];
var newArray = arrayX.map(i=> JSON.stringify(i)); //stringify all the objects so as to compare them
var res = newArray.filter((elem, index)=>{
  if(newArray.indexOf(elem) === newArray.lastIndexOf(elem)){
    return elem //get only those elements whihc do not have dupes
  }
}); 
var finalResult = res.map(i=>JSON.parse(i)); //parse the result to get the array of object
console.log(finalResult)
ABGR
  • 4,631
  • 4
  • 27
  • 49