En supposant que j'ai les éléments suivants:
var array =
[
{"name":"Joe", "age":17},
{"name":"Bob", "age":17},
{"name":"Carl", "age": 35}
]
Quel est le meilleur moyen de pouvoir obtenir un tableau de tous les âges, de manière à obtenir un tableau de résultats de:
[17, 35]
Existe-t-il un moyen de structurer alternativement les données ou une meilleure méthode, de sorte que je n'aurais pas à parcourir chaque tableau en vérifiant la valeur de "age" et en vérifiant son existence par rapport à un autre, et à l'ajouter sinon?
S'il y avait un moyen de sortir des âges différents sans itérer ...
Je voudrais améliorer la manière actuellement inefficace ... Si cela signifie qu'au lieu de "tableau", il s'agisse d'un tableau d'objets, mais d'une "carte" d'objets avec une clé unique (c'est-à-dire "1,2,3") qui serait ok aussi Je cherche simplement le moyen le plus efficace et performant.
Voici comment je le fais actuellement, mais pour moi, l'itération semble être minable pour l'efficacité même si cela fonctionne ...
var distinct = []
for (var i = 0; i < array.length; i++)
if (array[i].age not in distinct)
distinct.Push(array[i].age)
Si c'était PHP, je construirais un tableau avec les clés et prendrais array_keys
à la fin, mais JS n'a pas ce luxe. Au lieu de cela, essayez ceci:
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);
}
Si vous utilisez ES6/ES2015 ou une version ultérieure, procédez comme suit:
const unique = [...new Set(array.map(item => item.age))];
Ici est un exemple sur la façon de le faire.
Vous pouvez utiliser une approche de dictionnaire comme celle-ci. Fondamentalement, vous affectez la valeur que vous voulez distinguer comme clé dans un dictionnaire. Si la clé n'existait pas, vous ajoutez cette valeur distincte.
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;
}
Voici une démo de travail: http://jsfiddle.net/jbUKP/1
Ce sera O(n) où n est le nombre d'objets dans le tableau et m le nombre de valeurs uniques. Il n'y a pas de moyen plus rapide que O(n) car vous devez inspecter chaque valeur au moins une fois.
Performance
http://jsperf.com/filter-versus-dictionary Quand j'ai exécuté ce dictionnaire, il était 30% plus rapide.
en utilisant 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]
Je voudrais juste cartographier et supprimer les doublons:
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]
Modifier: Aight! Pas le moyen le plus efficace en termes de performances, mais le plus simple et le plus lisible OMI. Si vous vous souciez vraiment de la micro-optimisation ou que vous avez d'énormes quantités de données, une boucle for
régulière sera plus "efficace".
voici comment résoudre ce problème à l'aide du nouveau Set via ES6 pour TypeScript à compter du 25 août 2017.
Array.from(new Set(yourArray.map((item: any) => item.id)))
En utilisant les fonctionnalités de ES6, vous pouvez faire quelque chose comme:
const uniqueAges = [...new Set( array.map(obj => obj.age)) ];
La version forEach
de la réponse de @ travis-j (utile pour les navigateurs modernes et le monde Node JS):
var unique = {};
var distinct = [];
array.forEach(function (x) {
if (!unique[x.age]) {
distinct.Push(x.age);
unique[x.age] = true;
}
});
34% plus rapide sur Chrome v29.0.1547: http://jsperf.com/filter-versus-dictionary/3
Et une solution générique qui prend une fonction de mappeur (un peu plus lente que la carte directe, mais c'est prévu):
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]
J'ai commencé à coller Underscore dans tous les nouveaux projets par défaut, afin de ne jamais avoir à penser à ces petits problèmes d'élimination de données.
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());
Produit [17, 35]
.
en utilisant lodash
var array = [
{ "name": "Joe", "age": 17 },
{ "name": "Bob", "age": 17 },
{ "name": "Carl", "age": 35 }
];
_.chain(array).pluck('age').unique().value();
> [17, 35]
Voici un autre moyen de résoudre ceci:
var result = {};
for(var i in array) {
result[array[i].age] = null;
}
result = Object.keys(result);
Je ne sais pas à quelle vitesse cette solution est comparée aux autres, mais j'aime le look plus propre. ;-)
EDIT: OK, ce qui précède semble être la solution la plus lente de tous ici.
J'ai créé un scénario de test de performance ici: http://jsperf.com/distinct-values-from-array
Au lieu de tester pour les âges (Entiers), j'ai choisi de comparer les noms (Strings).
La méthode 1 (la solution de TS) est très rapide. Il est intéressant de noter que la méthode 7 surpasse toutes les autres solutions. Ici, je viens de me débarrasser de .indexOf () et d’utiliser une implémentation "manuelle" de celle-ci, en évitant les appels de fonctions en boucle:
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);
}
La différence de performances entre Safari et Firefox est incroyable, et il semble que Chrome fasse le meilleur travail possible en matière d'optimisation.
Je ne sais pas exactement pourquoi les extraits ci-dessus sont si rapides par rapport aux autres. Peut-être que quelqu'un de plus sage que moi a une réponse. ;-)
Il existe déjà de nombreuses réponses valables, mais je voulais en ajouter une qui utilise uniquement la méthode reduce()
car elle est propre et simple.
function uniqueBy(arr, prop){
return arr.reduce((a, d) => {
if (!a.includes(d[prop])) { a.Push(d[prop]); }
return a;
}, []);
}
Utilisez-le comme ceci:
var array = [
{"name": "Joe", "age": 17},
{"name": "Bob", "age": 17},
{"name": "Carl", "age": 35}
];
var ages = uniqueBy(array, "age");
console.log(ages); // [17, 35]
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;
}
Utiliser Lodash
var array = [
{ "name": "Joe", "age": 17 },
{ "name": "Bob", "age": 17 },
{ "name": "Carl", "age": 35 }
];
_.chain(array).map('age').unique().value();
Retours [17,35]
underscore.js _.uniq(_.pluck(array,"age"))
je pense que vous recherchez une fonction groupBy (en utilisant Lodash)
_personsList = [{"name":"Joe", "age":17},
{"name":"Bob", "age":17},
{"name":"Carl", "age": 35}];
_uniqAgeList = _.groupBy(_personsList,"age");
_uniqAges = Object.keys(_uniqAgeList);
produit le résultat:
17,35
démo jsFiddle: http://jsfiddle.net/4J2SX/201/
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))];
Si vous avez Array.prototype.includes ou êtes prêt à polyfill it, cela fonctionne:
var ages = []; array.forEach(function(x) { if (!ages.includes(x.age)) ages.Push(x.age); });
Si, comme moi, vous préférez une fonction plus "fonctionnelle" sans compromettre la vitesse, cet exemple utilise la recherche rapide par dictionnaire intégrée à la fermeture réduite.
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]
}
}, [{},[]])
Selon ce test ma solution est deux fois plus rapide que la réponse proposée
Je viens de trouver cela et j'ai pensé que c'était utile
_.map(_.indexBy(records, '_id'), function(obj){return obj})
Encore une fois en utilisant underscore , donc si vous avez un objet comme celui-ci
var records = [{_id:1,name:'one', _id:2,name:'two', _id:1,name:'one'}]
cela vous donnera uniquement les objets uniques.
Ce qui se passe ici, c'est que indexBy
renvoie une carte comme celle-ci
{ 1:{_id:1,name:'one'}, 2:{_id:2,name:'two'} }
et juste parce que c'est une carte, toutes les clés sont uniques.
Ensuite, je mappe simplement cette liste vers le tableau.
Au cas où vous n’auriez besoin que des valeurs distinctes
_.map(_.indexBy(records, '_id'), function(obj,key){return key})
Gardez à l’esprit que la variable key
est renvoyée sous forme de chaîne. Par conséquent, si vous avez besoin d’entiers, vous devriez le faire.
_.map(_.indexBy(records, '_id'), function(obj,key){return parseInt(key)})
Voici une solution polyvalente qui utilise réduire, permet le mappage et maintient l'ordre d'insertion.
items : Un tableau
mapper : Une fonction unaire qui mappe l'élément aux critères ou vide pour mapper l'élément lui-même.
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;
}, []);
}
Usage
const distinctLastNames = distinct(items, (item)=>item.lastName);
const distinctItems = distinct(items);
Vous pouvez ajouter ceci à votre prototype Array et omettre le paramètre items si c'est votre style ...
const distinctLastNames = items.distinct( (item)=>item.lastName) ) ;
const distinctItems = items.distinct() ;
Vous pouvez également utiliser un ensemble au lieu d'un tableau pour accélérer la correspondance.
function distinct(items, mapper) {
if (!mapper) mapper = (item)=>item;
return items.map(mapper).reduce((acc, item) => {
acc.add(item);
return acc;
}, new Set());
}
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;
}
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' }
]
*/
Mon code ci-dessous montrera le tableau unique des âges ainsi que le nouveau tableau n'ayant pas d'âge en double
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);```
Simple one-liner avec d'excellentes performances. 6% plus rapide que les solutions ES6 dans mes tests .
var ages = array.map (function (o) {return o.age}). filter (fonction (v, i, a) {return a.indexOf (v) === i});
Juste essayer
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);
Je sais que c'est une question ancienne et relativement bien répondue et la réponse que je donne récupèrera l'objet complet (ce que je vois suggéré dans de nombreux commentaires sur ce post). C'est peut-être "collant", mais en termes de lisibilité, cela semble beaucoup plus propre (bien que moins efficace) que beaucoup d'autres solutions.
Cela retournera un tableau unique des objets complets à l'intérieur du tableau.
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))```
Il existe une bibliothèque qui fournit des collections interrogeables fortement typées dans TypeScript.
Les collections sont:
La bibliothèque s'appelle ts-generic-collections .
Code source sur GitHub:
https://github.com/VeritasSoftware/ts-generic-collections
Vous pouvez obtenir des valeurs distinctes comme ci-dessous
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;
}
}
L'utilisation des nouvelles fonctionnalités d'Ecma est excellente, mais tous les utilisateurs ne disposent pas encore de celles disponibles.
Le code suivant va associer une nouvelle fonction nommée distinct à l'objet Tableau global . Si vous essayez d'obtenir des valeurs distinctes d'un tableau d'objets, vous pouvez passer le nom de la valeur pour obtenir les valeurs distinctes de cet type.
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;};
Découvrez mon post dans CodePen pour une démonstration.
J'ai écrit le mien dans TypeScript, pour un cas générique, comme celui de Kotlin Array.distinctBy {}
...
function distinctBy<T, U extends string | number>(array: T[], mapFn: (el: T) => U) {
const uniqueKeys = new Set(array.map(mapFn));
return array.filter((el) => uniqueKeys.has(mapFn(el)));
}
Où U
est bien sûr lavable. Pour les objets, vous aurez peut-être besoin de https://www.npmjs.com/package/es6-json-stable-stringify