# Array.prototype.uniqBy

**URL:** <https://es.discourse.group/t/array-prototype-uniqby/138>\
**Category:** 💡 Ideas\
**Created:** [November 22, 2019, 7:57pm UTC](https://es.discourse.group/t/array-prototype-uniqby/138 "2019-11-22T19:57:53Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![AndrewRot](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/andrewrot/32/256_2.png) [@AndrewRot](https://es.discourse.group/u/AndrewRot)\
**Post date:** [November 22, 2019, 7:57pm UTC](https://es.discourse.group/t/array-prototype-uniqby/138/1 "2019-11-22T19:57:53Z")

</div>

# Array.prototype.uniqBy-Proposal

## Intro to Problem

It would be useful to have the ability to get all unique objects according to some provided definition of equality.

Additional functionality:

```javascript
Array.prototype.uniqBy

```

## Where the problem is seen

There are many implementations of this function across Github, see [here](https://github.com/search?l=JavaScript&q=uniqBy&type=Code). This shows the desire for such functionality. Systems built around custom implementations of this could be subject to bugs or misunderstanding around the differences in equality methods used. This doesn’t account for other implementations of this method which are implemented using another name.

## What people are doing about it today

Developers are forced to either implement this functionality themselves, or rely on third party libraries such as Lodash, which offers the same interface as proposed here.

[Lodash’s uniqBy](https://www.npmjs.com/package/lodash.uniqby) function alone has almost half a million weekly downloads. This sits about high up the table for their most commonly used functions. It is their 7th most popular out of the 65 base functions the libraries comes with. See a comparison table for Lodash function popularity [here](https://github.com/AndrewRot/lodash-function-usage/blob/9ec92a7980e321f8a32dd62e13addd6562ba4612/README.md).

[Ramda’s uniqBy](https://ramdajs.com/docs/#uniqBy) is another popular function implementation. Their complete library has over 5 million weekly downloads.

## Solutions

There are a couple of ways this functionality could be implemented. Two implementations are listed below.

### Map to a canonical representative of an equivalence class:

One approach is to pass uniqBy a function which maps the objects in the array to a canonical representative of an equivalence class.

#### Examples

```javascript
[{a: 1, b: 1}, {a: 1, b: 2}].uniqBy(x => x.a); // [{a: 1, b: 1}]
[{a: 1, b: 1}, {a: 1, b: 2}].uniqBy(x => x.b); // [{a: 1, b: 1}, {a: 1, b: 2}]
[{a: 1, b: {c: 2}}, {a: 2, b: {c: 2}}].uniqBy(x => x.b.c); // [{a: 1, b: {c: 2}}]

```

Implementation 1.a:

```javascript
Array.prototype.uniqBy = function(generateRepresentative) {
  let representatives = this.map(generateRepresentative);
  let uniqueRepresentatives = Array.from(new Set(representatives));
  return uniqueRepresentatives
    .map(x => representatives.indexOf(x))
    .map(index => this[index]);
}

```

#### Difficulties

This implementation works well when mapping to a single value representative. However, becomes problematic when you wish to uniqBy a combination of values.

#### How immutable records/tuples makes this API great

This could be improved by using the [Immutable Records proposal](https://github.com/tc39/proposal-record-tuple). Immutable Records have built-in value equality which could be used in the following way to support uniqBy on multiple values:

Implementation 1.b:

```javascript
[{a: 1, b: 1}, {a: 1, b: 2}].uniqBy(x => #[x.a, x.b]); // [{a: 1, b: 1}, {a: 1, b: 2}]

```

Both versions of implementation 1 have O(n) time complexity.

### Provide a compare method

An alternative implementation of uniqBy would consume a comparison method which would determine whether two entries in the list should be considered equivalent. This way of implementation is referred to as `uniqWith` by both Lodash and Ramda.

#### Examples

```javascript
[{a: 1, b: 1}, {a: 1, b: 2}].uniqBy((x, y) => x.a === y.a); // [{a: 1, b: 1}]
[{a: 1, b: 1}, {a: 1, b: 2}].uniqBy((x, y) => x.b === y.b); // [{a: 1, b: 1}, {a: 1, b: 2}]
[{a: 1, b: {c: 2}}, {a: 2, b: {c: 2}}].uniqBy((x, y) => x.b.c === y.b.c); // [{a: 1, b: {c: 2}}]

```

Implementation 2.a:

```javascript
Array.prototype.uniqBy = function(compare) {
  let out = [];
  this.forEach(x => {
    if (!out.some(y => compare(x, y))) {
      out.push(x);
    }
  })
  return out;
}

```

#### Inefficiency of this API

This implementation yields an unideal performance time, ~O(n^2).

#### Usability of this API

The API allows for a comparator that does not actually define a partial ordering, which means a user could use this implementation in unintended ways. There are no safeguards around someone passing `(x, y) => x.a > y.a` to the compare function, which could yield confusing results.

### Additional information:

There is a node module, [array-unique](https://www.npmjs.com/package/array-unique), which has similar behavior. This module has over 13 million weekly downloads. However, this implementation only for doing so on a single value.

---

<div class="post-metadata">

**Author:** ![yulia](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/yulia/32/291_2.png) [@yulia](https://es.discourse.group/u/yulia)\
**Post date:** [December 9, 2019, 5:37am UTC](https://es.discourse.group/t/array-prototype-uniqby/138/2 "2019-12-09T05:37:53Z")

</div>

This looks pretty reasonable, And I don’t have any issues with either. though I don’t think uniqby should encompass both of these. Instead we would have to also do something like uniqWith. The example for the uniqWith behavior was hard to follow because it’s the same as uniqBy but it’s usecase is different, I think the load should documentation has a good example for anyone trying to understand: [https://lodash.com/docs#uniqWith](https://lodash.com/docs#uniqWith)

---

<div class="post-metadata">

**Author:** ![AndrewRot](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/andrewrot/32/256_2.png) [@AndrewRot](https://es.discourse.group/u/AndrewRot)\
**Post date:** [March 19, 2020, 9:34pm UTC](https://es.discourse.group/t/array-prototype-uniqby/138/3 "2020-03-19T21:34:11Z")

</div>

@yulia  
I would like to focus on just the `uniqBy` style API in this case. Assuming the proposal for immutable records/tuples makes it in, uniqBy can become quite effective.

Here of some more examples of the powerful functionality `uniqBy` can add in conjunction on its own and with [immutable records/tuples](https://github.com/tc39/proposal-record-tuple).

uniqBy (on its own):

```javascript
// ✅Works with: single field, top level properties
[{a: 1, b: 1}, {a: 1, b: 2}].uniqBy(x => x.a); // [{a: 1, b: 1}]
[{a: 1, b: 1}, {a: 1, b: 2}].uniqBy(x => x.b); // [{a: 1, b: 1}, {a: 1, b: 2}]

// ✅Works with: single field, nested properties
[{a: 1, b: {c: 2}}, {a: 2, b: {c: 2}}].uniqBy(x => x.b.c); // [{a: 1, b: {c: 2}}]

// ❌Doesn’t work with multiple fields
[{a: 1, b: 1}, {a: 1, b: 1}].uniqBy(x => { return { 'a': x.a, 'b': x.b }}); // [{a: 1, b: 1}, {a: 1, b: 1}]
// Incorrect result because using wrong equality comparison, should yield: [{a: 1, b: 1}]

```

uniqBy (with immutable records/tuples):

```javascript
// ✅Works with previous examples above
[{a: 1, b: 1}, {a: 1, b: 2}].uniqBy(x => x.a); // [{a: 1, b: 1}]
[{a: 1, b: 1}, {a: 1, b: 2}].uniqBy(x => x.b); // [{a: 1, b: 1}, {a: 1, b: 2}]
[{a: 1, b: {c: 2}}, {a: 2, b: {c: 2}}].uniqBy(x => x.b.c); // [{a: 1, b: {c: 2}}]

// ✅Works with single fields (using immutable records)
[{a: 1, b: 1}, {a: 1, b: 2}].uniqBy(x => #[x.a]); // [{a: 1}]

// ✅Works with multiple fields (using immutable records)
[{a: 1, b: 1}, {a: 1, b: 2}].uniqBy(x => #[x.a, x.b]); // [{a: 1, b: 1}, {a: 1, b: 2}]

```

---

<div class="post-metadata">

**Author:** ![timreichen](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/timreichen/32/316_2.png) [@timreichen](https://es.discourse.group/u/timreichen)\
**Post date:** [July 9, 2020, 7:58am UTC](https://es.discourse.group/t/array-prototype-uniqby/138/4 "2020-07-09T07:58:45Z")

</div>

Out of curiosity: It this was going to be a thing, would you consider renaming it `uniqueBy` or/and `uniqueWith` likewise? I think JS should avoid abbreviations in method names.

---

<div class="post-metadata">

**Author:** ![claudiameadows](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/claudiameadows/32/126_2.png) [@claudiameadows](https://es.discourse.group/u/claudiameadows)\
**Post date:** [July 10, 2020, 6:38am UTC](https://es.discourse.group/t/array-prototype-uniqby/138/5 "2020-07-10T06:38:27Z")

</div>

How is your `array.uniqBy((x, y) => x.key === y.key)` any different from `[...new Map(array.map(x => [x.key, x])).values()]`? Also, language precedent doesn't generally use a comparison function - they all use a key function as it's far more efficient.

---

<div class="post-metadata">

**Author:** ![TechQuery](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/techquery/32/385_2.png) [@TechQuery](https://es.discourse.group/u/TechQuery)\
**Post date:** [July 28, 2020, 9:34am UTC](https://es.discourse.group/t/array-prototype-uniqby/138/6 "2020-07-28T09:34:08Z")

</div>

I committed [a proposal](https://github.com/TechQuery/array-unique-proposal), which has been stage-1.
