私は以下のものを持っていると仮定します。
var array =
[
{"name":"Joe", "age":17},
{"name":"Bob", "age":17},
{"name":"Carl", "age": 35}
]
次のような結果配列が得られるように、すべての異なる年齢層の配列を取得できるようにするための最良の方法は何ですか。
[17, 35]
別の方法でデータやより良い方法を構築して、 "age"の値をチェックして別の配列に対して存在を確認し、そうでない場合はそれを追加する必要がないようにすることができますか。
何もしなければ、何も繰り返さずに年齢を区別することができます。
現在の非効率的な方法私は改善したいのですが...もしそれが "array"の代わりにオブジェクトの配列ではなく、いくつかのユニークなキー(すなわち "1,2,3")を持つオブジェクトの "map"であることを意味するなら大丈夫です。私はただ最も効率的な方法を探しています。
以下は私が現在どのようにそれをしているかですが、私にとっては、反復はうまくいっているとしても効率のために単に不器用であるように思われます...
var distinct = []
for (var i = 0; i < array.length; i++)
if (array[i].age not in distinct)
distinct.Push(array[i].age)
これが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);
}
ES6/ES2015以降を使用している場合は、次のようにして実行できます。
const unique = [...new Set(array.map(item => item.age))];
ここで はそのやり方の例です。
あなたはこのような辞書のアプローチを使うことができます。基本的には、区別したい値を辞書のキーとして割り当てます。キーが存在しない場合は、その値を別個のものとして追加します。
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)よりも早い方法はありません。それぞれの値を少なくとも1回検査する必要があるからです。
パフォーマンス
http://jsperf.com/filter-versus-dictionary 私がこの辞書を走らせたとき、30%早くなりました。
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]
これが、2017年8月25日現在、TypeScript用のES6を介した新しいSetを使用してこれを解決する方法です。
Array.from(new Set(yourArray.map((item: any) => item.id)))
ES6の機能を使うと、次のようなことができます。
const uniqueAges = [...new Set( array.map(obj => obj.age)) ];
マップを作成して重複を削除するだけです。
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
ループがより "効率的"になるでしょう。
@ travis-jの回答のforEach
バージョン(最近のブラウザとNode JSの世界で役に立ちます):
var unique = {};
var distinct = [];
array.forEach(function (x) {
if (!unique[x.age]) {
distinct.Push(x.age);
unique[x.age] = true;
}
});
Chrome v29.0.1547の34%高速な動作: 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]
私は アンダースコア をデフォルトですべての新しいプロジェクトに固執し始めたので、私はこれらの小さなデータ管理の問題について考える必要はありません。
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]
を生成します。
lodash を使用
var array = [
{ "name": "Joe", "age": 17 },
{ "name": "Bob", "age": 17 },
{ "name": "Carl", "age": 35 }
];
_.chain(array).pluck('age').unique().value();
> [17, 35]
有効な答えはたくさんありますが、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]
これを解決するもう1つの方法があります。
var result = {};
for(var i in array) {
result[array[i].age] = null;
}
result = Object.keys(result);
私はこのソリューションが他のものと比較してどれくらい速いのかわかりませんが、私はよりきれいな外観が好きです。 ;-)
編集:さて、上記はすべてここで最も遅い解決策のようです。
ここでパフォーマンステストケースを作成しました。 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が最適化に最も適しているようです。
上記のスニペットが他のものに比べて非常に高速な理由、私には答えがあるより賢い人がいるのかどうか、私にはよくわかりません。 ;-)
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;
}
Lodash を使う
var array = [
{ "name": "Joe", "age": 17 },
{ "name": "Bob", "age": 17 },
{ "name": "Carl", "age": 35 }
];
_.chain(array).map('age').unique().value();
戻り値[17,35]
underscore.js _.uniq(_.pluck(array,"age"))
私はあなたが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デモ: 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))];
あなたがArray.prototype.includeを持っているか、それを polyfill にするつもりなら、これはうまくいきます:
var ages = []; array.forEach(function(x) { if (!ages.includes(x.age)) ages.Push(x.age); });
私のようにスピードを落とさずにもっと "機能的"を好むのであれば、この例ではreduceクロージャーに囲まれた高速な辞書検索を使います。
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]
}
}, [{},[]])
これによると テスト 私の解決策は提案された答えの2倍の速さです
これを見つけただけで便利だと思いました
_.map(_.indexBy(records, '_id'), function(obj){return obj})
ここでも アンダースコア を使用します。このようなオブジェクトがある場合は
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)})
これは、reduceを使用し、マッピングを可能にし、挿入順序を維持する多用途のソリューションです。
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);
これを配列のプロトタイプに追加して、 items パラメータを省略しても構いません。
const distinctLastNames = items.distinct( (item)=>item.lastName) ) ;
const distinctItems = items.distinct() ;
マッチングを高速化するために、配列の代わりにSetを使用することもできます。
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' }
]
*/
高性能のシンプルなワンライナー。私の テストではES6ソリューションより6%速い 。
var ages = array.map(function(o){return o.age})。filter(function(v、i、a){return a.indexOf(v)=== i});
ちょうど試して
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);
私はこれが古くて比較的よく答えられた質問であることを知っています、そして、私が与えている答えは完全なオブジェクトを取り戻します(この投稿に関する多くのコメントで提案されたものを見ます)。それは「粘着性がある」かもしれませんが、読みやすさの点では、他の多くのソリューションよりもずっと効率的ではありますが(効率は劣ります)。
これにより、配列内の完全なオブジェクトの一意の配列が返されます。
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))```
TypeScriptで 強く型付けされた問い合わせ可能なコレクション を提供するライブラリがあります。
コレクションは以下のとおりです。
このライブラリは ts-generic-collections と呼ばれます。
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;
}
}
新しいEcma機能を使うのは素晴らしいことですが、すべてのユーザーがまだそれらを利用できるわけではありません。
次のコードは、 distinct という名前の新しい関数を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で 私の投稿 をチェックしてください。
KotlinのArray.distinctBy {}
...のような一般的なケースのために、私はTypeScriptで自分で書きました。
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)));
}
もちろん、U
はハッシュ可能です。オブジェクトの場合、 https://www.npmjs.com/package/es6-json-stable-stringify が必要になる場合があります
私の下のコードは重複した年齢を持たない新しい配列と同様に年齢のユニークな配列を示します
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);```