Как получить различные значения из массива объектов в JavaScript?

Предполагая, что у меня есть следующее:

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

Каков наилучший способ получить массив всех разных возрастов, так что я получаю массив результатов:

[17, 35]

Можно ли каким-либо образом структурировать данные или лучший метод, чтобы мне не пришлось перебирать каждый массив, проверяя значение "возраст" и проверяя на наличие другого массива для его существования и добавляя его, если нет?

Если бы был какой-то способ, я мог бы просто вытащить разные возрасты без повторения...

Нынешний неэффективный способ, который я хотел бы улучшить... Если это означает, что вместо "array" будет массив объектов, а "карта" объектов с каким-то уникальным ключом (т.е. "1,2,3" ) это тоже будет хорошо. Im просто ищет наиболее эффективный способ.

Ниже приводится информация о том, как я это делаю в настоящее время, но для меня итерация, как представляется, просто обременительна для эффективности, даже если она работает...

var distinct = []
for (var i = 0; i < array.length; i++)
   if (array[i].age not in distinct)
      distinct.push(array[i].age)

Ответ 1

Если бы это был PHP, я бы построил массив с ключами и взял array_keys в конце, но у JS такой роскоши нет. Вместо этого попробуйте следующее:

var flags = [], output = [], l = array.length, i;
for( i=0; i<l; i++) {
    if( flags[array[i].age]) continue;
    flags[array[i].age] = true;
    output.push(array[i].age);
}

Ответ 2

Если вы используете ES6/ES2015 или более позднюю версию, вы можете сделать это следующим образом:

const unique = [...new Set(array.map(item => item.age))];

Вот пример того, как это сделать.

Ответ 3

с использованием ES6

let array = [
  { "name": "Joe", "age": 17 },
  { "name": "Bob", "age": 17 },
  { "name": "Carl", "age": 35 }
];
array.map(item => item.age)
  .filter((value, index, self) => self.indexOf(value) === index)

> [17, 35]

Ответ 4

Вы можете использовать словарь, подобный этому. В основном вы назначаете значение, которое вы хотите отличить как ключ в словаре. Если ключ не существовал, вы добавляете это значение как отдельное.

var unique = {};
var distinct = [];
for( var i in array ){
 if( typeof(unique[array[i].age]) == "undefined"){
  distinct.push(array[i].age);
 }
 unique[array[i].age] = 0;
}

Вот рабочая демонстрация: http://jsfiddle.net/jbUKP/1

Это будет O (n), где n - количество объектов в массиве, а m - количество уникальных значений. Существует не более быстрый способ, чем O (n), потому что вы должны проверять каждое значение хотя бы один раз.

Производительность

http://jsperf.com/filter-versus-dictionary Когда я запускал этот словарь, он был на 30% быстрее.

Ответ 5

вот как вы могли бы решить это, используя новый Set через ES6 для Typescript по состоянию на 25 августа 2017 года

Array.from(new Set(yourArray.map((item: any) => item.id)))

Ответ 6

Используя функции ES6, вы можете сделать что-то вроде:

const uniqueAges = [...new Set( array.map(obj => obj.age)) ];

Ответ 7

Я бы просто отобразил и удалил дубликаты:

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

console.log(ages); //=> [17, 35]

Изменить: Aight! Не самый эффективный способ с точки зрения производительности, а самый простой и читаемый IMO. Если вы действительно заботитесь о микро-оптимизации или у вас есть огромное количество данных, тогда регулярный цикл for будет более "эффективным".

Ответ 8

var unique = array
    .map(p => p.age)
    .filter((age, index, arr) => arr.indexOf(age) == index)
    .sort(); // sorting is optional

// or in ES6

var unique = [...new Set(array.map(p => p.age))];

// or with lodash

var unique = _.uniq(_.map(array, 'age'));

Пример ES6

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

const arr = data.map(p => p.age); // [17, 17, 35]
const s = new Set(arr); // {17, 35} a set removes duplications, but it still a set
const unique = [...s]; // [17, 35] Use the spread operator to transform a set into an Array
// or use Array.from to transform a set into an array
const unique2 = Array.from(s); // [17, 35]

Ответ 9

forEach версия ответа @travis-j (полезно для современных браузеров и мира Node JS):

var unique = {};
var distinct = [];
array.forEach(function (x) {
  if (!unique[x.age]) {
    distinct.push(x.age);
    unique[x.age] = true;
  }
});

34% быстрее на Chrome v29.0.1547: http://jsperf.com/filter-versus-dictionary/3

И общее решение, которое принимает функцию сопоставления (медленнее, чем прямая карта, но ожидаемая):

function uniqueBy(arr, fn) {
  var unique = {};
  var distinct = [];
  arr.forEach(function (x) {
    var key = fn(x);
    if (!unique[key]) {
      distinct.push(key);
      unique[key] = true;
    }
  });
  return distinct;
}

// usage
uniqueBy(array, function(x){return x.age;}); // outputs [17, 35]

Ответ 10

Уже есть много правильных ответов, но я хотел бы добавить тот, который использует только метод reduce() потому что он чистый и простой.

function uniqueBy(arr, prop){
  return arr.reduce((a, d) => {
    if (!a.includes(d[prop])) { a.push(d[prop]); }
    return a;
  }, []);
}

Используйте это так:

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

var ages = uniqueBy(array, "age");
console.log(ages); // [17, 35]

Ответ 11

Я начал приклеивать Underscore во все новые проекты по умолчанию, так что мне никогда не нужно думать об этих проблемах с небольшим количеством данных.

var array = [{"name":"Joe", "age":17}, {"name":"Bob", "age":17}, {"name":"Carl", "age": 35}];
console.log(_.chain(array).map(function(item) { return item.age }).uniq().value());

Производит [17, 35].

Ответ 12

Вот еще один способ решить эту проблему:

var result = {};
for(var i in array) {
    result[array[i].age] = null;
}
result = Object.keys(result);

Я понятия не имею, насколько быстро это решение сравнивается с другими, но мне нравится более чистый вид.;-)


EDIT: Хорошо, вышеизложенное кажется самым медленным решением для всех здесь.

Я создал тестовый пример производительности: http://jsperf.com/distinct-values-from-array

Вместо тестирования для возрастов (целые числа) я решил сравнить имена (строки).

Метод 1 (решение TS) очень быстрый. Интересно, что метод 7 превосходит все другие решения, здесь я просто избавился от .indexOf() и использовал "ручную" реализацию, избегая циклической функции, вызывающей:

var result = [];
loop1: for (var i = 0; i < array.length; i++) {
    var name = array[i].name;
    for (var i2 = 0; i2 < result.length; i2++) {
        if (result[i2] == name) {
            continue loop1;
        }
    }
    result.push(name);
}

Разница в производительности с использованием Safari и Firefox удивительна, и похоже, что Chrome делает лучшую работу по оптимизации.

Я не совсем уверен, почему приведенные выше фрагменты так быстрее, по сравнению с другими, может быть, у кого-то мудрее, чем у меня есть ответ.; -)

Ответ 13

с помощью lodash

var array = [
    { "name": "Joe", "age": 17 },
    { "name": "Bob", "age": 17 },
    { "name": "Carl", "age": 35 }
];
_.chain(array).pluck('age').unique().value();
> [17, 35]

Ответ 14

function get_unique_values_from_array_object(array,property){
    var unique = {};
    var distinct = [];
    for( var i in array ){
       if( typeof(unique[array[i][property]]) == "undefined"){
          distinct.push(array[i]);
       }
       unique[array[i][property]] = 0;
    }
    return distinct;
}

Ответ 15

underscore.js _.uniq(_.pluck(array,"age"))

Ответ 16

Использование Lodash

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

_.chain(array).map('age').unique().value();

Возвращает [17,35]

Ответ 17

Я думаю, что вы ищете функцию GroupBy (используя Lodash)

_personsList = [{"name":"Joe", "age":17}, 
                {"name":"Bob", "age":17}, 
                {"name":"Carl", "age": 35}];
_uniqAgeList = _.groupBy(_personsList,"age");
_uniqAges = Object.keys(_uniqAgeList);

создает результат:

17,35

jsFiddle demo: http://jsfiddle.net/4J2SX/201/

Ответ 18

const x = [
  {"id":"93","name":"CVAM_NGP_KW"},
  {"id":"94","name":"CVAM_NGP_PB"},
  {"id":"93","name":"CVAM_NGP_KW"},
  {"id":"94","name":"CVAM_NGP_PB"}
].reduce(
  (accumulator, current) => accumulator.some(x => x.id === current.id)? accumulator: [...accumulator, current ], []
)

console.log(x)

/* output 
[ 
  { id: '93', name: 'CVAM_NGP_KW' },
  { id: '94', name: 'CVAM_NGP_PB' } 
]
*/

Ответ 19

Здесь универсальное решение, использующее сокращение, позволяет отображать и поддерживает порядок вставки.

items: массив

mapper: Унарная функция, которая отображает элемент в критерии или пуста, чтобы отобразить элемент.

function distinct(items, mapper) {
    if (!mapper) mapper = (item)=>item;
    return items.map(mapper).reduce((acc, item) => {
        if (acc.indexOf(item) === -1) acc.push(item);
        return acc;
    }, []);
}

Использование

const distinctLastNames = distinct(items, (item)=>item.lastName);
const distinctItems = distinct(items);

Вы можете добавить это в свой прототип Array и оставить параметр items, если ваш стиль...

const distinctLastNames = items.distinct( (item)=>item.lastName) ) ;
const distinctItems = items.distinct() ;

Вы также можете использовать набор вместо массива для ускорения сопоставления.

function distinct(items, mapper) {
    if (!mapper) mapper = (item)=>item;
    return items.map(mapper).reduce((acc, item) => {
        acc.add(item);
        return acc;
    }, new Set());
}

Ответ 20

Просто нашел это, и я подумал, что это полезно

_.map(_.indexBy(records, '_id'), function(obj){return obj})

Снова используя underscore, поэтому, если у вас есть такой объект

var records = [{_id:1,name:'one', _id:2,name:'two', _id:1,name:'one'}]

он предоставит вам уникальные объекты.

Что происходит, так это то, что indexBy возвращает такую ​​карту как

{ 1:{_id:1,name:'one'}, 2:{_id:2,name:'two'} }

и только потому, что это карта, все ключи уникальны.

Затем я просто сопоставляю этот список с массивом.

Если вам нужны только отдельные значения

_.map(_.indexBy(records, '_id'), function(obj,key){return key})

Имейте в виду, что key возвращается как строка, поэтому, если вам нужны целые числа, вы должны сделать

_.map(_.indexBy(records, '_id'), function(obj,key){return parseInt(key)})

Ответ 21

Если у вас есть Array.prototype.includes или готовы polyfill, это работает:

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

Ответ 22

Мой код ниже покажет уникальный массив возрастов, а также новый массив, не имеющий повторяющийся возраст

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

Ответ 23

Если мне нравится, что вы предпочитаете более "функциональный" без компромиссной скорости, в этом примере используется быстрый поиск в словаре, завернутый внутри уменьшения закрытия.

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]
    }
}, [{},[]])

В соответствии с этим test мое решение в два раза быстрее, чем предлагаемый ответ

Ответ 24

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

Ответ 25

Существует библиотека, которая предоставляет строго типизированные, запрашиваемые коллекции в машинописи.

Коллекции:

  • Список
  • толковый словарь

Библиотека называется ts-generic-collection.

Исходный код на GitHub:

https://github.com/VeritasSoftware/ts-generic-collections

Вы можете получить различные значения, как показано ниже

  it('distinct', () => {
    let numbers: number[] = [1, 2, 3, 1, 3];
    let list = new List(numbers);

    let distinct = list.distinct(new EqualityComparer());

    expect(distinct.length == 3);
    expect(distinct.elementAt(0) == 1);
    expect(distinct.elementAt(1) == 2);
    expect(distinct.elementAt(2) == 3);
  });

  class EqualityComparer implements IEqualityComparer<number> {
    equals(x: number, y: number) : boolean {
      return x == y;
    }
  }

Ответ 26

Использование новых функций Ecma отличное, но не у всех пользователей есть те, которые доступны.

Следующий код добавит новую функцию с именем Различный к объекту Global Array. Если вы пытаетесь получить различные значения массива объектов, вы можете передать имя значения, чтобы получить различные значения этого типа.

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

Откроем мой пост в CodePen для демонстрации.

Ответ 27

Просто попробуйте

var x = [] ;
for (var i = 0 ; i < array.length ; i++)
{
 if(x.indexOf(array[i]['age']) == -1)
  {
    x.push(array[i]['age']);
  }
}
console.log(x);

Ответ 28

npm install unique-by-property

Я только что опубликовал пакет сегодня.

Ответ 29

Простой однострочник с отличной производительностью. На 6% быстрее, чем решения ES6 в моих тестах.

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

Ответ 30

Я знаю, что это старый и относительно хорошо отвеченный вопрос, и ответ, который я даю, вернет объект целиком (который, как я вижу, предлагается во многих комментариях к этому сообщению). Это может быть "липким", но с точки зрения читабельности кажется намного чище (хотя и менее эффективным), чем многие другие решения.

Это вернет уникальный массив полных объектов внутри массива.

let productIds = data.map(d => { 
   return JSON.stringify({ 
      id    : d.sku.product.productId,
      name  : d.sku.product.name,
      price : '${d.sku.product.price.currency} ${(d.sku.product.price.gross / d.sku.product.price.divisor).toFixed(2)}'
   })
})
productIds = [ ...new Set(productIds)].map(d => JSON.parse(d))'''