Remove duplicates form an array

2020-01-25 12:38发布

问题:

I have an array of objects that looks like this:

var array = [
    {id:123, value:"value1", name:"Name1"},
    {id:124, value:"value2", name:"Name1"},
    {id:125, value:"value3", name:"Name2"},
    {id:126, value:"value4", name:"Name2"}
    ...
];

As you can see, some names are repeated. I want to get a new array with names only, but if some name repeats I don't want to add it again. I want this array:

var newArray = ["Name1", "Name2"];

I'm trying to do this with map:

var newArray = array.map((a) => {
    return a.name;
});

But the problem is that this returns:

newArray = ["Name1", "Name1", "Name2", "Name2"];

How can I set some condition inside map, so it won't return an element that already exists? I want to do this with map or some other ECMAScript 5 or ECMAScript 6 feature.

回答1:

With ES6, you could use Set for unique values, after mapping only the names of the objects.

This proposal uses a spread syntax ... for collecting the items in a new array.

const array = [{ id: 123, value: "value1", name:"Name1" }, { id: 124, value: "value2", name: "Name1" }, { id: 125, value: "value3", name: "Name2" }, { id: 126, value: "value4", name: "Name2" }],
      names = [...new Set(array.map(a => a.name))];

console.log(names);



回答2:

If you are looking for a JavaScript solution that is not ES 6 (no Set) you can use the Array's reduce method:

var array=[
  {id:123, value:"value1", name:"Name1"},
  {id:124, value:"value2", name:"Name1"},
  {id:125, value:"value3", name:"Name2"},
  {id:126, value:"value4", name:"Name2"}
];
var names = array.reduce(function (a, b) {
  if (a.indexOf(b.name) == -1) {
    a.push(b.name)
  }
  return a;
}, []);

console.log(names);



回答3:

Personally I don't see why everyone is getting all fancy with ES 6. If it were my code I'd prefer to support as many browsers as possible.

var array=[
{id:123, value:"value1", name:"Name1"},
{id:124, value:"value2", name:"Name1"},
{id:125, value:"value3", name:"Name2"},
{id:126, value:"value4", name:"Name2"}
];

   // Create array of unique names
var a = (function(a){
  for (var i = array.length; i--;)
    if (a.indexOf(array[i].name) < 0) a.push(array[i].name);
  return a;
})([]);

console.log(a);



回答4:

You could also simply combine map with filter

var array = [
  {id:123, value:"value1", name:"Name1"},
  {id:124, value:"value2", name:"Name1"},
  {id:125, value:"value3", name:"Name2"},
  {id:126, value:"value4", name:"Name2"}
];

var unique = array
  .map( item => item.name )
  .filter( ( item, idx, arr ) => arr.indexOf( item ) == idx ) 

console.log(unique)



回答5:

You can do Array.prototype.map() to get an array with the objects name property and than Array.prototype.filter() using the parameters elem, index and array, in the function predicate, to eliminate repeated elements:

var array = [{id:123, value:"value1", name:"Name1"}, {id:124, value:"value2", name:"Name1"}, {id:125, value:"value3", name:"Name2"}, {id:126, value:"value4", name:"Name2"}],
    names = array
      .map(e => e.name)
      .filter((e, i, a) => a.indexOf(e) === i);

console.log(names);



回答6:

Many good answers here. I just would like to contribute with some diversity with hopes to give you another perspective.

Arrays are of object type in JavaScript, so they can be used as a hash at the same time. By using this functionality we can greatly simplify the job to be done in a single reduce operation with O(n) time complexity.

If you are not happy with your array holding some properties other than the array keys you might consider keeping a separate hash object as well.

var array = [{id:123, value:"value1", name:"Name1"},
             {id:124, value:"value2", name:"Name1"},
             {id:125, value:"value3", name:"Name2"},
             {id:126, value:"value4", name:"Name2"}
            ],
result = array.reduce((p,c) => p[c.name] ? p : (p[c.name] = true, p.push(c.name), p), []);
console.log(result);



回答7:

I agree that if you only need the name values, a Set is the way to go.

However, if you want to get an array of unique objects based on the name property, I'd suggest to use a Map. A quick way to create a Map, is via an array of [key, value] arrays:

const array = [{ id: 123, value: "value1", name:"Name1" }, { id: 124, value: "value2", name: "Name1" }, { id: 125, value: "value3", name: "Name2" }, { id: 126, value: "value4", name: "Name2" }],
      unique = new Map(array.map(obj => [obj.name, obj]));

// To get the unique objects
const uniques = Array.from(unique.values());

// Get the names like you already did:
console.log("Names:", uniques.map(obj => obj.name));

// If you ever need the complete array of unique objects, you got a ref:
console.log(JSON.stringify(uniques));
.as-console-wrapper { min-height: 100%; }

An added benefit of Map is that you get both the filter functionality that cuts out the non-uniques, without loosing the connection with the source objects. Of course, it's only needed if you need to reference the unique set of objects multiple times.



回答8:

If you're limited to ES5, I would use Lodash's _.uniq

var newArray = _.uniq(array.map(function(a) {
  return a.name;
}));


回答9:

With ES6 this should do the job.

var array=[
    {id:123, value:"value1", name:"Name1"},
    {id:124, value:"value2", name:"Name1"},
    {id:125, value:"value3", name:"Name2"},
    {id:126, value:"value4", name:"Name2"}
];

var set = new Set();

array.forEach((a)=>{
    set.add(a.name);
}); 

console.log(Array.from(set));



回答10:

Using UnderscoreJS,

array = [{id:123, value:"value1", name:"Name1"}, {id:124, value:"value2", name:"Name1"}, {id:125, value:"value3", name:"Name2"}, {id:126, value:"value4", name:"Name2"}];
get_names =  _.pluck(_.uniq(array, 'name'), 'name')
console.log(get_names)
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>

`



回答11:

In ES5, use an object as a dictionary for O(n) performance.

This will only work if all the keys are Strings.

var array = [
    {id: 123, value: "value1", name: "Name1"},
    {id: 124, value: "value2", name: "Name1"},
    {id: 125, value: "value3", name: "Name2"},
    {id: 126, value: "value4", name: "Name2"}
];

var allNames = array.map(item => item.name);

var map = {};
allNames.forEach(name => {
  map[name] = true;
});
var uniqueNames = Object.keys(map);

console.log(uniqueNames);

You could do the same thing in one expression if you like:

var uniqueNames = Object.keys(allNames.reduce((m, n) => (m[n] = true, m), {}));

but I find the imperative form easier to read.



回答12:

Try this:

nArr = [];
array.forEach((a) => {
    if (nArr.indexOf(a.name) < 0) { 
        nArr.push(a.name); 
    }
}); 


回答13:

Use array#forEach() and array#indexOf() methods like this if you want maximum compatibility yet, concise syntax:

const array = [{ id: 123, value: "value1", name:"Name1" }, { id: 124, value: "value2", name: "Name1" }, { id: 125, value: "value3", name: "Name2" }, { id: 126, value: "value4", name: "Name2" }]

// initialize an empty array named names
let names = [];

// iterate through every element of `array` & check if it's 'name' key's value already in names array if not ADD it 
array.forEach(function(element) { if (names.indexOf(element.name) === -1) names.push(element.name) });
// or use tilde like this:
//array.forEach(function(element) { if (~names.indexOf(element.name)) names.push(element.name) });

console.log(names);

However, if compatibility is not an issue use ECMAScript 6's Set object, array#map and Array.from() methods like this:

const array = [{ id: 123, value: "value1", name:"Name1" }, { id: 124, value: "value2", name: "Name1" }, { id: 125, value: "value3", name: "Name2" }, { id: 126, value: "value4", name: "Name2" }];

// iterate through every element from array using map and store it in Set(a Set won't have duplicates) then convert the Set back to Array(using Array.from)
let names = Array.from(new Set(array.map(element => element.name)));

console.log(names);



回答14:

That's how I did it, using a separate empty array.

var array = [
   {id:123, value:"value1", name:"Name1"},
   {id:124, value:"value2", name:"Name1"},
   {id:125, value:"value3", name:"Name2"},
   {id:126, value:"value4", name:"Name2"}	    
];

var array2 = []		
		
for (i=0; i<array.length;i++){						
   if (array2.indexOf(array[i].name) == -1){				
     array2.push(array[i].name);
    }
}			

console.log(array2)	



回答15:

For those seeking a 1 liner

const names = array.reduce((acc, {name}) => acc.includes(name) ? acc : [name, ...acc], []);

or without using methods on the array's prototype

const { reduce, includes } = Array;
const names = reduce(array, (acc, {name}) => includes(acc, name) ? acc : [name, ...acc], []);

could be usefull for writing some pure functions for dealing with this

const get_uniq_values = (key, arr) => reduce(arr, (a, o) => includes(a, o[key]) ? a : [o[key], ...a], []);


回答16:

var __array=[{id:123, value:"value1", name:"Name1"},{id:124, value:"value2", name:"Name1"},{id:125, value:"value3", name:"Name2"},{id:126, value:"value4", name:"Name2"}];

function __checkArray(__obj){
    var flag = true;
    for(let i=0; i < __array.length; i++){
        if(__obj.id == __array.id){
            flag = false;
            break;
        }
    }

    return flag;
}

var __valToPush = {id: 127, value: "value5", name: "Name3"};
if(__checkArray(__valToPush)){
    __array.push(__valToPush)
}