Stephan-v
Stephan-v

Reputation: 20289

How to remove duplicates objects in array based on 2 properties?

I have an array of room objects and I am currently removing duplicates objects from the array based on their room_rate_type_id property:

const rooms = [{
    room_rate_type_id: 202,
    price: 200
  },
  {
    room_rate_type_id: 202,
    price: 200
  },
  {
    room_rate_type_id: 202,
    price: 189
  },
  {
    room_rate_type_id: 190,
    price: 200
  }
];

const newRooms = rooms.filter((room, index, array) => {
  const roomRateTypeIds = rooms.map(room => room.room_rate_type_id);
  // Returns the first index found.
  return roomRateTypeIds.indexOf(room.room_rate_type_id) === index;
});

console.log(newRooms);

However I also need to make sure that objects only get removed if not only their room_rate_type_id matches but also their price.

I can understand how the filter functionality works in my given example but I am unsure how to cleanly do a check for the price as well, preferably in ES6.

Upvotes: 6

Views: 5147

Answers (6)

Marko Savic
Marko Savic

Reputation: 2384

const rooms = [
  {
    room_rate_type_id: 202,
    price: 200
  },
  {
    room_rate_type_id: 202,
    price: 200
  },
  {
    room_rate_type_id: 202,
    price: 189
  },
  {
    room_rate_type_id: 190,
    price: 200
  }
];

let newRooms = rooms.filter((x, i, arr) => arr.findIndex(y => y.room_rate_type_id === x.room_rate_type_id && y.price === x.price) === i);


console.log(newRooms);

Upvotes: 0

Ori Drori
Ori Drori

Reputation: 191916

You can reduce the array to a Map object by creating a key from both properties, and adding the object to the Map only if the key doesn't already exist. Then spread the Map#values back to an array:

const rooms = [{
    room_rate_type_id: 202,
    price: 200
  },
  {
    room_rate_type_id: 202,
    price: 200
  },
  {
    room_rate_type_id: 202,
    price: 189
  },
  {
    room_rate_type_id: 190,
    price: 200
  }
];

const newRooms = [...rooms.reduce((m, r) => {
  const key = `${r.room_rate_type_id}-${r.price}`; // create the key by combining both props
  return m.has(key) ? m : m.set(key, r); // if key exists skip, if not add to map
}, new Map()).values()]; // get the map values and convert back to array

console.log(newRooms);

Upvotes: 4

Olian04
Olian04

Reputation: 6862

This would do it:

const rooms = [{
    room_rate_type_id: 202,
    price: 200
  },
  {
    room_rate_type_id: 202,
    price: 200
  },
  {
    room_rate_type_id: 202,
    price: 189
  },
  {
    room_rate_type_id: 190,
    price: 200
  }
];

const newRooms = rooms.reduce((rooms, room) => {
  let l = rooms.filter(r => {
    return r.room_rate_type_id === room.room_rate_type_id && r.price === room.price;
  });
  if (l.length === 0) {
    return [...rooms, room]
  }
  return rooms;
}, [rooms[0]]);

console.log(newRooms);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 0

RomanPerekhrest
RomanPerekhrest

Reputation: 92854

Simple approach: using concatenation of room_rate_type_id and price keys as unique key:

const rooms = [
    {room_rate_type_id: 202,price: 200},{room_rate_type_id: 202,price: 200},{room_rate_type_id: 202,price: 189},{room_rate_type_id: 190,price: 200}
];

const roomRateKeys = [];
const newRooms = rooms.filter((r, i, a) => {
    var k = r.room_rate_type_id + "" + r.price;
    if (roomRateKeys.indexOf(k) === -1) {
        roomRateKeys.push(k);
	return r;
    }
});

console.log(newRooms);

Upvotes: 1

T.J. Crowder
T.J. Crowder

Reputation: 1073968

For a smallish array, you can do it by repeating looking for other matching rooms:

const newRooms = rooms.filter((room, index) => {
  // Only include this room if there isn't another room earlier
  // in the array that has the same values
  return !rooms.some((r, i) =>
    i < index &&
    r.room_rate_type_id == room.room_rate_type_id &&
    r.price == room.price
  );
});

const rooms = [{
    room_rate_type_id: 202,
    price: 200
  },
  {
    room_rate_type_id: 202,
    price: 200
  },
  {
    room_rate_type_id: 202,
    price: 189
  },
  {
    room_rate_type_id: 190,
    price: 200
  }
];

const newRooms = rooms.filter((room, index) => {
  // Only include this room if there isn't another room earlier
  // in the array that has the same values
  return !rooms.some((r, i) =>
    i < index &&
    r.room_rate_type_id == room.room_rate_type_id &&
    r.price == room.price
  );
});

console.log(newRooms);
.as-console-wrapper {
  max-height: 100% !important;
}

If the array is really large, that gets inefficient and you may be better off remembering combinations you've seen before rather than constantly re-searching the array:

const seenRooms = Object.create(null);
const newRooms = rooms.filter((room, index) => {
  const key = room.room_rate_type_id + "**" + room.price;
  if (seenRooms[key]) {
    return false;
  }
  seenRooms[key] = true;
  return true;
});

const rooms = [{
    room_rate_type_id: 202,
    price: 200
  },
  {
    room_rate_type_id: 202,
    price: 200
  },
  {
    room_rate_type_id: 202,
    price: 189
  },
  {
    room_rate_type_id: 190,
    price: 200
  }
];

const seenRooms = Object.create(null);
const newRooms = rooms.filter((room, index) => {
  const key = room.room_rate_type_id + "**" + room.price;
  if (seenRooms[key]) {
    return false;
  }
  seenRooms[key] = true;
  return true;
});

console.log(newRooms);
.as-console-wrapper {
  max-height: 100% !important;
}

Those are written for clarity; you can make them more concise if you like.

Upvotes: 2

marvel308
marvel308

Reputation: 10458

You can do

const rooms = [
  {
    room_rate_type_id: 202,
    price: 200
  },
  {
    room_rate_type_id: 202,
    price: 200
  },
  {
    room_rate_type_id: 202,
    price: 189
  },
  {
    room_rate_type_id: 190,
    price: 200
  }
];

let result = rooms.filter((e, i) => {
    return rooms.findIndex((x) => {
    return x.room_rate_type_id == e.room_rate_type_id && x.price == e.price;}) == i;

});

console.log(result);

This would filter all duplicates except the first occurrence of any object

Upvotes: 14

Related Questions