Видаліть дублікати з масиву


100

У мене є масив об'єктів, який виглядає приблизно так:

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 newArray = ["Name1", "Name2"];

Я намагаюся зробити це за допомогою map:

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

Але проблема полягає в тому, що це повертає:

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

Як я можу встановити деяку умову всередині map, щоб він не повернув елемент, який вже існує? Я хочу це зробити за допомогою mapбудь-якої іншої функції ECMAScript 5 або ECMAScript 6.


2
Потім вийміть дублікати з масиву.
Тушар



Чому рядок, що містить id: 125, не закінчується комою?
Пітер Мортенсен

Зверніть увагу, що всі відповіді, що використовують .indexOf (), матимуть низьку продуктивність, якщо масив великий, через їх квадратичну складність у часі . Я рекомендую використовувати набір ES6 або використовувати об’єкт ES5 як словник.
joeytwiddle

Відповіді:


210

За допомогою ES6 ви можете використовувати Setдля унікальних значень після відображення лише назви об'єктів.

Ця пропозиція використовує синтаксис поширення... для збору елементів у новому масиві.

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);


9
Для моєї інформації .. що ...робити?
Раджешекар Редді

9
@RajshekarReddy, це синтаксис поширення... для створення масиву з ітерабельним об'єктом.
Ніна Шольц

5
Я не виступаю за сам "Set", а навпаки, за розумне використання оператора розповсюдження. Завжди приємно дізнатися щось нове або побачити щось нове, застосоване до практичного прикладу, тому ця публікація заслуговує на підсумок.
briosheje

33
Це лише моя особиста думка, але досить поширена . Використання Array.fromпередає наміри перетворення набагато краще (а також легше зрозуміти / шукати новачків), синтаксис розповсюдження повинен використовуватися лише там, де розповсюджений елемент є одним із багатьох. Можливо, називати це "поганою практикою" - це занадто суворо, вибачте, я просто сподіваюся не допустити, щоб це стало загальною ідіомою.
Бергі

3
@KRyan ES6 такий новий? Він був завершений у 2015 році. Пора почати розуміння та використання нових функцій
edc65

64

Якщо ви шукаєте рішення JavaScript, яке не є ES 6 (без набору), ви можете використовувати метод масивуreduce :

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);


2
Ця відповідь також має перевагу в тому, що він не вимагає проміжних масивів або наборів, особливо тих, що містять лише самі імена. (Це переходить безпосередньо від масиву об'єктів до масиву об’єктів.) +1
jpmc26

@Iwrestledabearonce, чи не цей другий парам є той самий об’єкт, що і повернутий?
Каїдо

Так, але це не переходить прямо з одного в інший, як сортування чи фільтр, його перебудовано з порожнього масиву.
Я боровся ведмедя одного разу.

1
@Dekel - Менша версія :)var names = array.reduce(function(a, b) { a.indexOf(b.name) === -1 && a.push(b.name) return a; }, []);
Район

3
@Rayon Будь ласка, не звертайте надто великої уваги на розмір вихідного коду, це загальна проблема для багатьох саморобних програмістів, головне, щоб ваша програма була ефективною та читаною.
лист

16

Особисто я не бачу, чому всі захоплюються ES 6. Якби це мій код, я вважаю за краще підтримувати якомога більше браузерів.

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);


2
Чи не буде більш ефективним використовувати об'єкт як словник, а не indexOf-ing весь час? Наприклад , робити added[name] = trueі if(added[name])замість if(a.indexOf(name)).
Божидар Маринов

7
"особисто я не бачу, чому всі отримують фантазію з es6", чому ви ухиляєтесь goto fail, просочуєте свою змінну індексу в область, що додається, і т.д. частина тривіально проста для заповнення / перетворення для старих браузерів.
Джаред Сміт

5
Більш читабельний? Це - його вигадка для циклу, який штовхає елементи на масив. Не стає набагато читабельніше, ніж це ......
Я боровся ведмедя одного разу.

4
Досить чесно, це просто відчуваєш, що ти занадто важко намагається знайти щось, на що скаржитися.
Я боровся ведмедя одного разу.

2
"Я маю на увазі знамениту помилку Apple SSL" - Це не так відомо, що ви можете просто перейти і посилатися на неї у випадкових коментарях поза контекстом і очікувати, що люди її впізнають.
Hejazzman

14

Крім того, можна просто об'єднати mapз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)


11

Ви можете використовувати Object.keys (), щоб отримати масив власних перелічених імен властивостей даного об'єкта з результату об'єкта ітераційної arrayзмінної за допомогою Array.prototype.reduce (), де ключі - знищені імена

Код:

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 = Object.keys(array.reduce((a, { name }) => (a[name] = 1, a), {}))

console.log(names)


7

Тут багато хороших відповідей. Я просто хотів би внести певне розмаїття з надією дати вам іншу перспективу.

Масиви є об’єктним типом у JavaScript, тому їх можна використовувати як хеш одночасно. Використовуючи цю функціональність, ми можемо значно спростити роботу, яку потрібно виконати за одну операцію скорочення, що має O (n) часову складність.

Якщо ви не задоволені тим, що ваш масив містить деякі властивості, окрім клавіш масиву, ви також можете зберегти окремий хеш-об’єкт.

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);


Це рішення ES5 має хороші показники, але використання масиву для двох цілей є заплутаним та небезпечним, якщо одне з імен є лише числом. Я рекомендую робити p[c.name]речі на окремому об'єкті, oа потім відкидати їх.
joeytwiddle

7

Я погоджуюся, що якщо вам потрібні лише nameзначення, Setшлях - це шлях.

Однак , якщо ви хочете отримати масив унікальних об'єктів на основі nameвластивості, я б запропонував скористатися Map. Швидкий спосіб створити карту через масив [key, value]масивів:

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%; }

Додатковою перевагою Mapє те, що ви отримуєте як filterфункціонал, який вирізає неоднакові, не втрачаючи зв’язку з вихідними об'єктами. Звичайно, це потрібно лише в тому випадку, якщо вам потрібно кілька разів посилатися на унікальний набір об'єктів.



2

З ES6 це повинно зробити свою роботу.

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));


11
mapпередбачається використовувати з чистими функціями, не мати побічних ефектів. Це була б робота для forEachабо reduce.
Вінсент ван дер Веель

1

Використовуючи 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>

`


0

У ES5 використовуйте об'єкт як словник для виконання O ( n ).

Це буде працювати лише в тому випадку, якщо всі клавіші - це рядки.

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);

Ви можете зробити те саме в одному виразі, якщо вам подобається:

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

але я вважаю, що імперативна форма легша для читання.


Для наочності я використовував функції стрілок ES6. Якщо ви дійсно орієнтуєтесь на ES5 без транспілера, тоді вам потрібно буде використовувати повну форму замість цього:function (item) { return item.name; }
joeytwiddle

В якості альтернативи Object.keys(), ця відповідь використань filter()зберегти тільки ті імена , які ще не були збережені на карті, яка досить елегантно.
joeytwiddle


0

Використання array#forEach()і array#indexOf()методи , як це , якщо ви хочете максимальну сумісність ще, лаконічний синтаксис:

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);

Однак, якщо сумісність не є проблемою використання ECMAScript 6 «s Setоб'єкта, array#mapі такі Array.from()методи , як це:

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);


0

Ось як я це зробив, використовуючи окремий порожній масив.

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)	


-1

Для тих, хто шукає 1 лайнер

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

або без використання методів прототипу масиву

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

може бути корисним для написання чистих функцій для вирішення цього питання

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

-1
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)
}

11
у вас є особлива причина використовувати стільки підкреслень?
Ніна Шольц
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.