web-dev-qa-db-ja.com

プロパティごとに一意のオブジェクトの配列を作成する

次のようなオブジェクトの配列を作成しました。

var places = [];
var a = {};
a.lat = 12.123;
a.lng = 13.213;
a.city = "New York";

places.Push(a);

var b = {};
b.lat = 3.123;
b.lng = 2.213;
b.city = "New York";

places.Push(b);

...

同じ都市プロパティを持たないオブジェクトのみを含むように場所をフィルタリングする新しい配列を作成しようとしています(緯度/経度の重複は問題ありません)。これを実現するための組み込みのJSまたはJquery関数はありますか?

31
theblueone

フィルタリング中に、おそらく次のようにフラグオブジェクトを使用します。

var flags = {};
var newPlaces = places.filter(function(entry) {
    if (flags[entry.city]) {
        return false;
    }
    flags[entry.city] = true;
    return true;
});

これは、ECMAScript5(ES5)の Array#filter を使用します。これは、シムできるES5の追加の1つです(いくつかのオプションについては「es5シム」を検索してください)。

filterなしでもできます。もちろん、もう少し冗長です。

var flags = {};
var newPlaces = [];
var index;
for (index = 0; index < places.length; ++index) {
    if (!flags[entry.city]) {
        flags[entry.city] = true;
        newPlaces.Push(entry);
    }
});

上記の両方は、特定の都市のfirstオブジェクトを保持し、他のすべてを破棄することを前提としています。


注:以下でuser2736012が指摘しているように、私のテストif (flags[entry.city])は、toStringなどのObject.prototypeに存在するプロパティと同じ名前の都市に対して真になります。この場合はほとんどありませんが、可能性を回避する方法は4つあります。

  • (私の通常の推奨ソリューション)プロトタイプなしでオブジェクトを作成します:var flags = Object.create(null);。これはES5の機能です。 IE8のような廃止されたブラウザでは、これをシムできないことに注意してください(引数の値がnullの場合、Object.createの単一引数バージョンはexceptになります) )。

  • テストにはhasOwnPropertyを使用します。 if (flags.hasOwnProperty(entry.city))

  • xxなど、Object.prototypeプロパティに存在しないことがわかっているものにプレフィックスを付けます。

    var key = "xx" + entry.city;
    if (flags[key]) {
        // ...
    }
    flags[key] = true;
    
  • ES2015以降、代わりにSetを使用できます:

    const flags = new Set();
    const newPlaces = places.filter(entry => {
        if (flags.has(entry.city)) {
            return false;
        }
        flags.add(entry.city);
        return true;
    });
    
46
T.J. Crowder

最短、しかし最高のパフォーマンスではない (以下の更新を参照)es6のソリューション:

function unique(array, propertyName) {
   return array.filter((e, i) => array.findIndex(a => a[propertyName] === e[propertyName]) === i);
}

パフォーマンス: https://jsperf.com/compare-unique-array-by-property

28
IgorL

@IgorLソリューションを少し拡張しましたが、プロトタイプを拡張し、プロパティの代わりにセレクター関数を与えて、柔軟性を少し高めました。

Array.prototype.unique = function(selector) {
   return this.filter((e, i) => this.findIndex((a) => {
      if (selector) {
        return selector(a) === selector(e);
      }
      return a === e;
    }) === i);
};

使用法:

// with no param it uses strict equals (===) against the object
let primArr = ['one','one','two','three','one']
primArr.unique() // ['one','two','three']

let a = {foo:123}
let b = {foo:123}
let fooArr = [a,a,b]
fooArr.unique() //[a,b]

// alternatively, you can pass a selector function
fooArr.unique(item=>item.foo) //[{foo:123}] (first "unique" item returned)

これを行う最も確実な方法ではありませんが、セレクターが単純で配列が大規模でない限り、正常に機能します。

TypeScriptで

Array.prototype.unique = function<T>(this: T[], selector?: (item: T) => object): T[] {
   return this.filter((e, i) => this.findIndex((a) => {
      if (selector) {
        return selector(a) === selector(e);
      }
      return a === e;
    }) === i);
};
4
NSjonas

https://lodash.com/docs#uniqBy

https://github.com/lodash/lodash/blob/4.13.1/lodash.js#L7711

/**
 * This method is like `_.uniq` except that it accepts `iteratee` which is
 * invoked for each element in `array` to generate the criterion by which
 * uniqueness is computed. The iteratee is invoked with one argument: (value).
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Array
 * @param {Array} array The array to inspect.
 * @param {Array|Function|Object|string} [iteratee=_.identity]
 *  The iteratee invoked per element.
 * @returns {Array} Returns the new duplicate free array.
 * @example
 *
 * _.uniqBy([2.1, 1.2, 2.3], Math.floor);
 * // => [2.1, 1.2]
 *
 * // The `_.property` iteratee shorthand.
 * _.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
 * // => [{ 'x': 1 }, { 'x': 2 }]
 */
3
Alec Perkey

私のおすすめ :

Array.prototype.uniqueCity = function() {
    var processed = [];
    for (var i=this.length-1; i>=0; i--){
        if (processed.indexOf(this[i].city)<0) {
            processed.Push(this[i].city);
        } else {
            this.splice(i, 1);
        }
    }
}

使用中で :

places.uniqueCity();

または

Array.prototype.uniqueObjectArray = function(field) {
    var processed = [];
    for (var i=this.length-1; i>=0; i--) {
        if (this[i].hasOwnProperty(field)) {
            if (processed.indexOf(this[i][field])<0) {
                processed.Push(this[i][field]);
            } else {
                this.splice(i, 1);
            }
        }
    }
}

places.uniqueObjectArray('city');

上記を使用すると、オブジェクト内の任意のフィールドで配列をソートできますオブジェクトの一部に存在しない場合でも

または

function uniqueCity(array) {
    var processed = [];
    for (var i=array.length-1; i>=0; i--){
        if (processed.indexOf(array[i].city)<0) {
            processed.Push(array[i].city);
        } else {
            array.splice(i, 1);
        }
    }
    return array;
}

places = uniqueCity(places);
3
davidkonrad

Mapを使用して、同じキープロパティ(この場合は「city」)を持つエントリが1回だけ表示されるようにすることができます

module.exports = (array, prop) => {
   const keyValueArray = array.map(entry => [entry[prop], entry]);
   const map = new Map(keyValueArray);
   return Array.from(map.values());
};

Mapオブジェクトと配列オブジェクトの詳細 here

Codepenの基本的な例

1
Tamo Maes
var places = [];
var a = {};
a.lat = 12.123;
a.lng = 13.213;
a.city = "New York";

places.Push(a);

var b = {};
b.lat = 3.123;
b.lng = 2.213;
b.city = "New York";

places.Push(b);

getUniqAR(places,'city'); //Return Uniq Array by property

function getUniqAR(Data,filter){
var uniar =[];
Data.forEach(function(item,ind,arr){
    var dupi=false;
    if(!uniar.length) uniar.Push(item) //Push first obj into uniq array 
    uniar.forEach(function(item2, ind2,arr){
    if(item2[filter] == item[filter]){  //check each obj prop of uniq array 
      dupi=true; //if values are same put duplicate is true
        }     
    })
if(!dupi){  uniar.Push(item)} //if no duplicate insert to uniq

})
console.log(uniar)
return uniar;
}
1
pandian_Snkl

コメントで指摘されているように、オブジェクトをマップとして使用すると、重複を避けることができ、オブジェクトのプロパティを列挙できます。

作業フィドル: http://jsfiddle.net/gPRPQ/1/

var places = [];
var a = {};
a.lat = 12.123;
a.lng = 13.213;
a.city = "New York";

places.Push(a);

var b = {};
b.lat = 3.123;
b.lng = 2.213;
b.city = "New York";

places.Push(b);

var unique = {}

for (var i = 0; i < places.length; i++) {
    var place = places[i];
    unique[place.city] = place;
}

for (var name in unique) {
    var place = unique[name];
    console.log(place);
}
1
Robert Byrne

別のオプション:

const uniqueBy = prop => list => {
    const uniques = {}
    return list.reduce(
        (result, item) => {
            if (uniques[item[prop]]) return result
            uniques[item[prop]] = item
            return [...result, item]
        },
        [],
    )
}

const uniqueById = uniqueBy('id')

uniqueById([
    { id: 1, name: 'one' },
    { id: 2, name: 'two' },
    { id: 1, name: 'one' },
    { id: 3, name: 'three' }
])

コンソールに貼り付けて、動作を確認できます。提示されたシナリオおよび他のいくつかのシナリオで機能するはずです。

1
rafaelbiten

単純なJavascriptコードでは、places配列リストから重複する都市を削除します

var places = [{ 'lat': 12.123, 'lng': 13.213, 'city': "New York"},
                { 'lat': 3.123, 'lng': 2.213, 'city': "New York"},
                { 'lat': 43.123, 'lng': 12.213, 'city': "London"}];
var unique = [];
var tempArr = [];
places.forEach((value, index) => {
    if (unique.indexOf(value.city) === -1) {
        unique.Push(value.city);
    } else {
        tempArr.Push(index);    
    }
});
tempArr.reverse();
tempArr.forEach(ele => {
    places.splice(ele, 1);
});
console.log(places);
0
Shridhar Sagari