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


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.