admin管理员组

文章数量:1287583

Trying to filter an array of objects by eliminating objects that have a specific property that exists already in another object (duplicate). The decision which object to remove should be based on another property.

Example: For an array of objects, that might look like this, the goal would be to filter all "user" duplicates and keep the one with the oldest "date" property.

const arr = [
    {user: 'Alex', date: '1540801929945'},
    {user: 'Bill', date: '1640801929946'},
    {user: 'Carl', date: '1740801929947'},
    {user: 'Alex', date: '1840801929948'},
]

Expected oute:

filteredArr = [
    {user: 'Alex', date: '1540801929945'},
    {user: 'Bill', date: '1640801929946'},
    {user: 'Carl', date: '1740801929947'},
]

I managed the basic filtering that works for keeping only unique objects.

const filteredArr = arr.reduce((unique, o) => {
    if(!unique.some(obj => obj.user === o.user) {
      unique.push(o);
    }
    return unique;
},[]);

Though I can't figure out, what to do so that in the case of a duplicate the "oldest" object stays and the latest get removed. Thanks so much for your help! Really appreciated.

Trying to filter an array of objects by eliminating objects that have a specific property that exists already in another object (duplicate). The decision which object to remove should be based on another property.

Example: For an array of objects, that might look like this, the goal would be to filter all "user" duplicates and keep the one with the oldest "date" property.

const arr = [
    {user: 'Alex', date: '1540801929945'},
    {user: 'Bill', date: '1640801929946'},
    {user: 'Carl', date: '1740801929947'},
    {user: 'Alex', date: '1840801929948'},
]

Expected oute:

filteredArr = [
    {user: 'Alex', date: '1540801929945'},
    {user: 'Bill', date: '1640801929946'},
    {user: 'Carl', date: '1740801929947'},
]

I managed the basic filtering that works for keeping only unique objects.

const filteredArr = arr.reduce((unique, o) => {
    if(!unique.some(obj => obj.user === o.user) {
      unique.push(o);
    }
    return unique;
},[]);

Though I can't figure out, what to do so that in the case of a duplicate the "oldest" object stays and the latest get removed. Thanks so much for your help! Really appreciated.

Share Improve this question edited Oct 29, 2018 at 9:27 YvonC asked Oct 29, 2018 at 9:22 YvonCYvonC 3492 gold badges6 silver badges23 bronze badges 4
  • is your data sorted by date? – Nina Scholz Commented Oct 29, 2018 at 9:25
  • No unfortunately not. The objects are in real usecase much bigger and sorted in different ways depending on where used... – YvonC Commented Oct 29, 2018 at 9:29
  • do you habe only the given two properties or more? do you want to keep the objects? – Nina Scholz Commented Oct 29, 2018 at 9:34
  • Yes, there are more properties. Don't plan to keep the objects. – YvonC Commented Oct 29, 2018 at 9:44
Add a ment  | 

4 Answers 4

Reset to default 7

Reduce the array to an object with the user as the key. If a user is not in unique object, or if it's date is "older" than the what that is, add it to the object. Convert the object to array with Object.values().

Note: since you the dates are string, convert them to a number while paring (I use the + operator).

const array = [{ user: 'Alex', date: '1540801929945' }, { user: 'Bill', date: '1640801929946' }, { user: 'Carl', date: '1740801929947' }, { user: 'Alex', date: '1840801929948' }];


const filteredArray = Object.values(array.reduce((unique, o) => {
  if(!unique[o.user] || +o.date > +unique[o.user].date) unique[o.user] = o;
  
  return unique;
}, {}));

console.log(filteredArray);

A solution with Map and a single loop approach

var array = [{ user: 'Alex', date: '1540801929945' }, { user: 'Bill', date: '1640801929946' }, { user: 'Carl', date: '1740801929947' }, { user: 'Alex', date: '1840801929948' }],
    filtered = Array.from(array
        .reduce((m, o) => !m.has(o.user) || m.get(o.user).data > o.date
            ? m.set(o.user, o)
            : m, new Map)
       .values());
      
console.log(filtered);
.as-console-wrapper { max-height: 100% !important; top: 0; }

For sorted data (take sorting in advance by date), you could use a Set for filtering.

var array = [{ user: 'Alex', date: '1540801929945' }, { user: 'Bill', date: '1640801929946' }, { user: 'Carl', date: '1740801929947' }, { user: 'Alex', date: '1840801929948' }],
    filtered = array
        .sort(({ date: a }, { date: b }) => a - b)
        .filter((s => ({ user }) => !s.has(user) && s.add(user))(new Set));
      
console.log(filtered);
.as-console-wrapper { max-height: 100% !important; top: 0; }

For an O(N) solution, reduce into an object indexed by user, whose values are dates - on each iteration, if something at that user already exists, keep only the lowest date. Then, iterate over the object's entries to turn it back into an array:

const arr = [
    {user: 'Alex', date: '1540801929945'},
    {user: 'Bill', date: '1640801929946'},
    {user: 'Carl', date: '1740801929947'},
    {user: 'Alex', date: '1840801929948'},
    {user: 'Carl', date: '1340801929947'},
];

const arrByUser = arr.reduce((a, { user, date }) => {
  if (!a[user]) a[user] = date;
  else if (a[user].localeCompare(date) === 1) a[user] = date;
  return a;
}, {});
const output = Object.entries(arrByUser)
  .map(([user, date]) => ({ user, date }));
console.log(output);

I believe you actually need to know the index of the non-unique element (Object with name "Alex" in your case) and then pare their dates, saving the older one.

Array.prototype.find wil lnot do you mouch good in this case. However, Array.prototype.find can be of help since it takes a callback which returns a boolean( just like 'some') BUT it returns the index of the element matched by your callback, or undefined if no such element is found.

Give it a try, it cant hurt :)

本文标签: