Как получить различные значения из массива объектов в 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))'''