896

Assuming I have the following:

var array = 
    [
        {"name":"Joe", "age":17}, 
        {"name":"Bob", "age":17}, 
        {"name":"Carl", "age": 35}
    ]

What is the best way to be able to get an array of all of the distinct ages such that I get an result array of:

[17, 35]

Is there some way I could alternatively structure the data or better method such that I would not have to iterate through each array checking the value of "age" and check against another array for its existence, and add it if not?

If there was some way I could just pull out the distinct ages without iterating...

Current inefficient way I would like to improve... If it means that instead of "array" being an array of objects, but a "map" of objects with some unique key (i.e. "1,2,3") that would be okay too. I'm just looking for the most performance efficient way.

The following is how I currently do it, but for me, iteration appears to just be crummy for efficiency even though it does work...

var distinct = []
for (var i = 0; i < array.length; i++)
   if (array[i].age not in distinct)
      distinct.push(array[i].age)
6
  • 78
    iteration isn't "crummy for efficiency" and you can't do anything to every element "without iterating". you can use various functional-looking methods, but ultimately, something on some level has to iterate over the items. Commented Feb 28, 2013 at 1:47
  • //100% running code const listOfTags = [{ id: 1, label: "Hello", color: "red", sorting: 0 }, { id: 2, label: "World", color: "green", sorting: 1 }, { id: 3, label: "Hello", color: "blue", sorting: 4 }, { id: 4, label: "Sunshine", color: "yellow", sorting: 5 }, { id: 5, label: "Hello", color: "red", sorting: 6 }], keys = ['label', 'color'], filtered = listOfTags.filter( (s => o => (k => !s.has(k) && s.add(k)) (keys.map(k => o[k]).join('|')) ) (new Set) ); console.log(filtered); Commented Nov 7, 2019 at 12:15
  • 2
    the bounty is great, but the question with the given data and answer is already answered here: stackoverflow.com/questions/53542882/…. what is the purpose of the bounty? should i answer this particular problem with two or more keys? Commented Nov 8, 2019 at 8:59
  • 2
    Set object and maps are wasteful. This job just takes a simple .reduce() stage. Commented Nov 13, 2019 at 19:37
  • Please check this example, stackoverflow.com/a/58944998/13013258 . Commented May 19, 2021 at 8:08

65 Answers 65

4

    var array = 
    [
        {"name":"Joe", "age":17}, 
        {"name":"Bob", "age":17}, 
        {"name":"Carl", "age": 35}
    ]

    console.log(Object.keys(array.reduce((r,{age}) => (r[age]='', r) , {})))

Output:

Array ["17", "35"]
Sign up to request clarification or add additional context in comments.

Comments

4

There are several options of course, but reduce() with Map() option seems to be performant as well:

const array = [
  { "name": "Joe", "age": 17 },
  { "name": "Bob", "age": 17 },
  { "name": "Carl", "age": 35 }
]

const key = 'age'

const redUniq = [
  ...array
    .reduce((uniq, curr) => {
      if (!uniq.has(curr[key])) {
        uniq.set(curr[key], curr);
      }
      return uniq;
    }, new Map())
    .values()
];

console.log(redUniq); //Output: [ { name: 'Joe', age: 17 }, { name: 'Carl', age: 35 } ]

see comparison with times/perf: https://replit.com/@tokra1/Comparison-of-Deduplicate-array-of-objects-by-specific-key

Comments

3

If you have Array.prototype.includes or are willing to polyfill it, this works:

var ages = []; array.forEach(function(x) { if (!ages.includes(x.age)) ages.push(x.age); });

Comments

3

If like me you prefer a more "functional" without compromising speed, this example uses fast dictionary lookup wrapped inside reduce closure.

var array = 
[
    {"name":"Joe", "age":17}, 
    {"name":"Bob", "age":17}, 
    {"name":"Carl", "age": 35}
]
var uniqueAges = array.reduce((p,c,i,a) => {
    if(!p[0][c.age]) {
        p[1].push(p[0][c.age] = c.age);
    }
    if(i<a.length-1) {
        return p
    } else {
        return p[1]
    }
}, [{},[]])

According to this test my solution is twice as fast as the proposed answer

Comments

3

I know my code is little length and little time complexity but it's understandable so I tried this way.

I'm trying to develop prototype based function here and code also change.

Here,Distinct is my own prototype function.

<script>
  var array = [{
      "name": "Joe",
      "age": 17
    },
    {
      "name": "Bob",
      "age": 17
    },
    {
      "name": "Carl",
      "age": 35
    }
  ]

  Array.prototype.Distinct = () => {
    var output = [];
    for (let i = 0; i < array.length; i++) {
      let flag = true;
      for (let j = 0; j < output.length; j++) {
        if (array[i].age == output[j]) {
          flag = false;
          break;
        }
      }
      if (flag)
        output.push(array[i].age);
    }
    return output;
  }
  //Distinct is my own function
  console.log(array.Distinct());
</script>

Comments

3

Simple one-liner with great performance. 6% faster than the ES6 solutions in my tests.

var ages = array.map(function(o){return o.age}).filter(function(v,i,a) {
    return a.indexOf(v)===i
});

2 Comments

@Jeb50 Care to add a multi-line that is easy to read? Looking at the others here I really don't feel they are easy to read or understand. I think it's best to place this in a function that describes what it does.
With arrow functions: array.map( o => o.age).filter( (v,i,a) => a.indexOf(v)===i). I use the function keyword so rarely now that I have to read things twice when I see it 😊
3

const array = [{
    "name": "Joe",
    "age": 17
  },
  {
    "name": "Bob",
    "age": 17
  },
  {
    "name": "Carl",
    "age": 35
  }
]

const uniqueArrayByProperty = (array, callback) => {
  return array.reduce((prev, item) => {
    const v = callback(item);    
    if (!prev.includes(v)) prev.push(v)          
    return prev
  }, [])    
}

console.log(uniqueArrayByProperty(array, it => it.age));

Comments

2

My below code will show the unique array of ages as well as new array not having duplicate age

var data = [
  {"name": "Joe", "age": 17}, 
  {"name": "Bob", "age": 17}, 
  {"name": "Carl", "age": 35}
];

var unique = [];
var tempArr = [];
data.forEach((value, index) => {
    if (unique.indexOf(value.age) === -1) {
        unique.push(value.age);
    } else {
        tempArr.push(index);    
    }
});
tempArr.reverse();
tempArr.forEach(ele => {
    data.splice(ele, 1);
});
console.log('Unique Ages', unique);
console.log('Unique Array', data);```

Comments

2

There are a lot of great answers here, but none of them have addressed the following line:

Is there some way I could alternatively structure the data

I would create an object whose keys are the ages, each pointing to an array of names.

var array = [{ "name": "Joe", "age": 17 }, { "name": "Bob", "age": 17 }, { "name": "Carl", "age": 35 }];

var map = array.reduce(function(result, item) {
  result[item.age] = result[item.age] || [];
  result[item.age].push(item.name);
  return result;
}, {});

console.log(Object.keys(map));
console.log(map);

This way you've converted the data structure into one that is very easy to retrieve the distinct ages from.

Here is a more compact version that also stores the entire object instead of just the name (in case you are dealing with objects with more than 2 properties so they cant be stored as key and value).

var array = [{ "name": "Joe", "age": 17 }, { "name": "Bob", "age": 17 }, { "name": "Carl", "age": 35 }];

var map = array.reduce((r, i) => ((r[i.age] = r[i.age] || []).push(i), r), {});

console.log(Object.keys(map));
console.log(map);

Comments

2

The simplest way I suggest is by creating an object on top of Array of elements by which you can get all the unique/distinct elements by the field. e.g.

const getDistinct = (arr = [], field) => {
    const distinctMap = {};
    arr.forEach(item => {
        if(!distinctMap[item[field]]) {
            distinctMap[item[field]] = item;
        }
    });
    const distinctFields = Object.keys(distinctMap);
    const distinctElements = Object.values(distinctMap);
    return {distinctFields, distinctElements}
}
const array = 
    [
        {"name":"Joe", "age":17}, 
        {"name":"Bob", "age":17}, 
        {"name":"Carl", "age": 35}
    ]
console.log(JSON.stringify(getDistinct(array, "age")));
/*output will be => 
  {
    distinctFields: ["17", "35"],
    distinctElements: [
      { name: "Joe", age: 17 },
      { name: "Carl", age: 35 }
    ]
  }*/

Comments

1

I wrote my own in TypeScript, for a generic case, like that in Kotlin's Array.distinctBy {}...

function distinctBy<T, U extends string | number>(array: T[], mapFn: (el: T) => U) {
  const uniqueKeys = new Set(array.map(mapFn));
  return array.filter((el) => uniqueKeys.has(mapFn(el)));
}

Where U is hashable, of course. For Objects, you might need https://www.npmjs.com/package/es6-json-stable-stringify

1 Comment

Does this actually work though? Your array filter checks if the key of the element is in the set of unique keys, won't this always be true, even for duplicates?
1

In case you need unique of whole object

const _ = require('lodash');

var objects = [
  { 'x': 1, 'y': 2 },
  { 'y': 1, 'x': 2 },
  { 'x': 2, 'y': 1 },
  { 'x': 1, 'y': 2 }
];

_.uniqWith(objects, _.isEqual);

[Object {x: 1, y: 2}, Object {x: 2, y: 1}]

Comments

1

Answering this old question is pretty pointless, but there is a simple answer that speaks to the nature of Javascript. Objects in Javascript are inherently hash tables. We can use this to get a hash of unique keys:

var o = {}; array.map(function(v){ o[v.age] = 1; });

Then we can reduce the hash to an array of unique values:

var a2 = []; for (k in o){ a2.push(k); }

That is all you need. The array a2 contains just the unique ages.

Comments

1

const array = [
  { "name": "Joe", "age": 17 }, 
  { "name":"Bob", "age":17 },
  { "name":"Carl", "age": 35 }
]

const allAges = array.map(a => a.age);

const uniqueSet = new Set(allAges)
const uniqueArray = [...uniqueSet]

console.log(uniqueArray)

Comments

1

There is linq.js - LINQ for JavaScript package (npm install linq), that should be familiar for .Net developers.

Among others methods shown in samples there are distinct overloads.

An example to distinct objects by property value from an array of objects is

Enumerable.from(array).distinct(“$.id”).toArray();

From https://medium.com/@xmedeko/i-recommend-you-to-try-https-github-com-mihaifm-linq-20a4e3c090e9

Comments

1

I picked up random samples and tested it against the 100,000 items as below:

let array=[]
for (var i=1;i<100000;i++){

 let j= Math.floor(Math.random() * i) + 1
  array.push({"name":"Joe"+j, "age":j})
}

And here the performance result for each:

  Vlad Bezden Time:         === > 15ms
  Travis J Time: 25ms       === > 25ms 
  Niet the Dark Absol Time: === > 30ms
  Arun Saini Time:          === > 31ms
  Mrchief Time:             === > 54ms
  Ivan Nosov Time:          === > 14374ms

Also, I want to mention, since the items are generated randomly, the second place was iterating between Travis and Niet.

Comments

1

If you're stuck using ES5, or you can't use new Set or new Map for some reason, and you need an array containing values with a unique key (and not just an array of unique keys), you can use the following:

function distinctBy(key, array) {
    var keys = array.map(function (value) { return value[key]; });
    return array.filter(function (value, index) { return keys.indexOf(value[key]) === index; });
}

Or the type-safe equivalent in TypeScript:

public distinctBy<T>(key: keyof T, array: T[]) {
    const keys = array.map(value => value[key]);
    return array.filter((value, index) => keys.indexOf(value[key]) === index);
}

Usage:

var distinctPeople = distinctBy('age', people);

All of the other answers either:

  • Return an array of unique keys instead of objects (like returning the list of ages instead of the people who have an unique age);
  • Use ES6, new Set, new Map etc. which might not be available to you;
  • Do not have a configurable key (like having .age hardcoded into the distinct function);
  • Assume the key can be used to index an array, which is not always true and TypeScript wouldn't allow it.

This answers does not have any of the four issues above.

Comments

1

Let assume we have data something like this arr=[{id:1,age:17},{id:2,age:19} ...], then we can find unique objects like this -

function getUniqueObjects(ObjectArray) {
    let uniqueIds = new Set();
    const list = [...new Set(ObjectArray.filter(obj => {
        if (!uniqueIds.has(obj.id)) {
            uniqueIds.add(obj.id);
            return obj;
        }
    }))];

    return list;
}

Check here Codepen Link

Comments

1

@Travis J dictionary answer in a Typescript type safety functional approach

const uniqueBy = <T, K extends keyof any>(
  list: T[] = [],
  getKey: (item: T) => K,
) => {
  return list.reduce((previous, currentItem) => {
    const keyValue = getKey(currentItem)
    const { uniqueMap, result } = previous
    const alreadyHas = uniqueMap[keyValue]
    if (alreadyHas) return previous
    return {
      result: [...result, currentItem],
      uniqueMap: { ...uniqueMap, [keyValue]: true }
    }
  }, { uniqueMap: {} as Record<K, any>, result: [] as T[] }).result
}

const array = [{ "name": "Joe", "age": 17 }, { "name": "Bob", "age": 17 }, { "name": "Carl", "age": 35 }];

console.log(uniqueBy(array, el => el.age))

// [
//     {
//         "name": "Joe",
//         "age": 17
//     },
//     {
//         "name": "Carl",
//         "age": 35
//     }
// ]

Comments

1

Presently working on a typescript library to query js objects in an orm fashion. You can download from the below link. This answer explains how to solve using the below library.

https://www.npmjs.com/package/@krishnadaspc/jsonquery?activeTab=readme

var ageArray = 
    [
        {"name":"Joe", "age":17}, 
        {"name":"Bob", "age":17}, 
        {"name":"Carl", "age": 35}
    ]

const ageArrayObj = new JSONQuery(ageArray)
console.log(ageArrayObj.distinct("age").get()) // outputs: [ { name: 'Bob', age: 17 }, { name: 'Carl', age: 35 } ]

console.log(ageArrayObj.distinct("age").fetchOnly("age")) // outputs: [ 17, 35 ]

Runkit live link: https://runkit.com/pckrishnadas88/639b5b3f8ef36f0008b17512

Comments

1

TypeScript

export const distinct = <TItem>(
    items: Array<TItem>, 
    mapper?: (item: TItem) => void) => {
    if (!mapper) mapper = it => it
    const distinctKeys = new Set(items.map(mapper))
    const distinctItems = Array.from(distinctKeys).reduce((arr, curr) => {
        const distinctItem = items.find(it => mapper!(it) === curr) as TItem
        arr.push(distinctItem)
        return arr
    }, new Array<TItem>())
    return distinctItems
}

Usage:

const arr = [1, 1, 2, 0, 1, 3]
distinct(arr)

Usage with key:

const arr = [{"id": "id1"}, {"id": "id2"}]
distinct(arr, item => item.id)

Comments

0

Using new Ecma features are great but not all users have those available yet.

Following code will attach a new function named distinct to the Global Array object. If you are trying get distinct values of an array of objects, you can pass the name of the value to get the distinct values of that type.

Array.prototype.distinct = function(item){   var results = [];
for (var i = 0, l = this.length; i < l; i++)
    if (!item){
        if (results.indexOf(this[i]) === -1)
            results.push(this[i]);
        } else {
        if (results.indexOf(this[i][item]) === -1)
            results.push(this[i][item]);
    }
return results;};

Check out my post in CodePen for a demo.

2 Comments

This is by far the fastest and most reusable way to do this.
jsbench.github.io/#e6f583e740e107b4f6eabd655114f35d can show how this will run like 70% faster than other methods.
0
unique(obj, prop) {
    let result = [];
    let seen = new Set();

    Object.keys(obj)
        .forEach((key) => {
            let value = obj[key];

            let test = !prop
                ? value
                : value[prop];

            !seen.has(test)
                && seen.add(test)
                && result.push(value);
        });

    return result;
}

Comments

0

Well you can use lodash to write a code which will be less verbose

Approach 1:Nested Approach

    let array = 
        [
            {"name":"Joe", "age":17}, 
            {"name":"Bob", "age":17}, 
            {"name":"Carl", "age": 35}
        ]
    let result = _.uniq(_.map(array,item=>item.age))

Approach 2: Method Chaining or Cascading method

    let array = 
        [
            {"name":"Joe", "age":17}, 
            {"name":"Bob", "age":17}, 
            {"name":"Carl", "age": 35}
        ]
    let result = _.chain(array).map(item=>item.age).uniq().value()

You can read about lodash's uniq() method from https://lodash.com/docs/4.17.15#uniq

Comments

0

The approach for getting a collection of distinct value from a group of keys.

You could take the given code from here and add a mapping for only the wanted keys to get an array of unique object values.

const
    listOfTags = [{ id: 1, label: "Hello", color: "red", sorting: 0 }, { id: 2, label: "World", color: "green", sorting: 1 }, { id: 3, label: "Hello", color: "blue", sorting: 4 }, { id: 4, label: "Sunshine", color: "yellow", sorting: 5 }, { id: 5, label: "Hello", color: "red", sorting: 6 }],
    keys = ['label', 'color'],
    filtered = listOfTags.filter(
        (s => o =>
            (k => !s.has(k) && s.add(k))
            (keys.map(k => o[k]).join('|'))
        )(new Set)
    )
    result = filtered.map(o => Object.fromEntries(keys.map(k => [k, o[k]])));

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

Comments

0

Using a set and filter. This preserves order:

let unique = (items) => {
    const s = new Set();
    return items.filter((item) => {
      if (s.has(item)) {
        return false;
      }
      s.add(item);
      return true;
    });
  }
  
  console.log(
  unique(
      [
        'one', 'two', 'two', 'three', 'three', 'three'
      ]
    )
  );

/*
output:
[
  "one",
  "two",
  "three"
]
*/

Comments

0

If your array is object array, you can use this code.

getUniqueArray = (array: MyData[]) => {
    return array.filter((elem, index) => array.findIndex(obj => obj.value == elem.value) === index);
}

Where MyData is like as below:

export interface MyData{
    value: string,
    name: string
}

Note: You can't use Set because when objects are compared they are compared by reference and not by value. Therefore you need unique key for compare objects, in my example unique key is value field. For more details, you can visit this link : Filter an array for unique values in Javascript

Comments

0

let mobilePhones = [{id: 1, brand: "B1"}, {id: 2, brand: "B2"}, {id: 3, brand: "B1"}, {id: 4, brand: "B1"}, {id: 5, brand: "B2"}, {id: 6, brand: "B3"}]
 let allBrandsArr = mobilePhones .map(row=>{
        return  row.brand;
      });
let uniqueBrands =   allBrandsArr.filter((item, index, arry) => (arry.indexOf(item) === index));
console.log('uniqueBrands   ', uniqueBrands );

Comments

0

Efficient and clean approach, using iter-ops library:

import {pipe, distinct, map} from 'iter-ops';

const array = 
    [
        {name: 'Joe', age: 17}, 
        {name: 'Bob', age: 17}, 
        {name: 'Carl', age: 35}
    ];

const i = pipe(
    array,
    distinct(a => a.age),
    map(m => m.age)
);

const uniqueAges = [...i]; //=> [17, 35]

Comments

0

Now we can Unique the Object on the base of same keys and same values

 const arr = [{"name":"Joe", "age":17},{"name":"Bob", "age":17}, {"name":"Carl", "age": 35},{"name":"Joe", "age":17}]
    let unique = []
     for (let char of arr) {
     let check = unique.find(e=> JSON.stringify(e) == JSON.stringify(char))
     if(!check) {
     unique.push(char)
     }
     }
    console.log(unique)

////outPut::: [{ name: "Joe", age: 17 }, { name: "Bob", age: 17 },{ name: "Carl", age: 35 }]

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.