web-dev-qa-db-ja.com

オブジェクトのJavaScript配列で重複する値を見つけて、一意の値のみを出力する方法

私はJSを学んでいます。以下のオブジェクトの配列があると仮定します:

var family = [
  {
    name: "Mike",
    age: 10
  },
  {
    name: "Matt"
    age: 13
  },
  {
    name: "Nancy",
    age: 15
  },
  {
    name: "Adam",
    age: 22
  },
  {
    name: "Jenny",
    age: 85
  },
  {
    name: "Nancy",
    age: 2
  },
  {
    name: "Carl",
    age: 40
  }
];

Nancyが2回表示されることに注意してください(年齢のみが変更されます)。一意の名前のみを出力するとします。上記のオブジェクトの配列を重複せずに出力するにはどうすればよいですか? ES6がを歓迎します

関連(オブジェクトの使用に適した方法が見つかりませんでした):

[〜#〜] edit [〜#〜]これが私が試したものです。それは文字列でうまく機能しますが、オブジェクトでそれを動作させる方法はわかりません:

family.reduce((a, b) => {
  if (a.indexOf(b) < 0 ) {
    a.Push(b);
  }
  return a;
},[]);
7
Rico Letterman

SetArray#map および スプレッド演算子...と組み合わせて使用​​できます単線。

Mapはすべての名前を持つ配列を返します。これらの名前はセット初期化子に入り、セットのすべての値が配列で返されます。

var family = [{ name: "Mike", age: 10 }, { name: "Matt", age: 13 }, { name: "Nancy", age: 15 }, { name: "Adam", age: 22 }, { name: "Jenny", age: 85 }, { name: "Nancy", age: 2 }, { name: "Carl", age: 40 }],
    unique = [...new Set(family.map(a => a.name))];

console.log(unique);

フィルタリングして一意の名前のみを返すには、- Array#filterSet を使用できます。

var family = [{ name: "Mike", age: 10 }, { name: "Matt", age: 13 }, { name: "Nancy", age: 15 }, { name: "Adam", age: 22 }, { name: "Jenny", age: 85 }, { name: "Nancy", age: 2 }, { name: "Carl", age: 40 }],
    unique = family.filter((set => f => !set.has(f.name) && set.add(f.name))(new Set));

console.log(unique);
25
Nina Scholz

ソリューション

ループの外部のnameの出現をオブジェクトに保存し、以前に出現した場合はフィルターします。

https://jsfiddle.net/nputptbb/2/

_var occurrences = {}

var filteredFamily = family.filter(function(x) {
  if (occurrences[x.name]) {
    return false;
  }
  occurrences[x.name] = true;
  return true;
})
_

このソリューションを関数に一般化することもできます

_function filterByProperty(array, propertyName) {
  var occurrences = {}

  return array.filter(function(x) {
    var property = x[propertyName]
    if (occurrences[property]) {
      return false;
    }
    occurrences[property]] = true;
    return true;
  })
}
_

そしてそれを次のように使用します

_var filteredFamily = filterByProperty(family, 'name')
_

説明

オブジェクト間で_===_演算子のみを使用するindexOfを使用してオブジェクトを比較しないでください。現在の答えが機能しない理由は、JSの_===_はオブジェクトを深く比較するのではなく、参照を比較するためです。つまり、次のコードを見るとわかります。

_var a = { x: 1 }
var b = { x: 1 }

console.log(a === b) // false
console.log(a === a) // true
_

同じexactオブジェクトが見つかった場合、Equalityは通知しますが、同じ内容のオブジェクトが見つかった場合は通知しません。

この場合、name上のオブジェクトは一意のキーである必要があるため、比較できます。したがって、_obj.name === obj.name_の代わりに_obj === obj_です。さらに、その機能ではなくランタイムに影響するコードの別の問題は、indexOfの内部でreduceを使用することです。 indexOfO(n)です。これにより、アルゴリズムの複雑さがO(n^2)になります。したがって、O(1)ルックアップを持つオブジェクトを使用することをお勧めします。

11
m0meni

これは正常に機能します。

const result = [1, 2, 2, 3, 3, 3, 3].reduce((x, y) => x.includes(y) ? x : [...x, y], []);

console.log(result);
5
Divyanshu Rawat

Lodashユーザーのための2つの簡単な方法を考えました

この配列を指定すると:

let family = [
  {
    name: "Mike",
    age: 10
  },
  {
    name: "Matt",
    age: 13
  },
  {
    name: "Nancy",
    age: 15
  },
  {
    name: "Adam",
    age: 22
  },
  {
    name: "Jenny",
    age: 85
  },
  {
    name: "Nancy",
    age: 2
  },
  {
    name: "Carl",
    age: 40
  }
]

1。重複を見つける:

let duplicatesArr = _.difference(family, _.uniqBy(family, 'name'), 'name')

// duplicatesArr:
// [{ 
//   name: "Nancy", 
//   age: 2 
// }]

2検証のために、重複があるかどうかを検索します:

let uniqArr = _.uniqBy(family, 'name')

if (uniqArr.length === family.length) {
    // No duplicates
}

if (uniqArr.length !== family.length) {
    // Has duplicates
}
3
Simon Dragsbæk

あなたが言及したコードで、あなたは試すことができます:

_family.filter((item, index, array) => {
  return array.map((mapItem) => mapItem['name']).indexOf(item['name']) === index
})
_

または、他のオブジェクトの配列に対しても機能させる汎用関数を使用できます。

_function printUniqueResults (arrayOfObj, key) {
  return arrayOfObj.filter((item, index, array) => {
    return array.map((mapItem) => mapItem[key]).indexOf(item[key]) === index
  })
}
_

そして、printUniqueResults(family, 'name')を使用します

(FIDDLE)

1
Cezar Augusto

私はおそらく何らかのオブジェクトをセットアップするでしょう。 ECMAScript 6と言ったので、Setにアクセスできますが、オブジェクトの値を比較したいので、それよりも少し手間がかかります。

例は次のようになります(わかりやすくするために名前空間のパターンを削除しました)。

var setOfValues = new Set();
var items = [];

function add(item, valueGetter) {
   var value = valueGetter(item);
   if (setOfValues.has(value))
      return;

   setOfValues.add(value);
   items.Push(item);
}

function addMany(items, valueGetter) {
   items.forEach(item => add(item, valueGetter));
}

次のように使用します。

var family = [
...
];

addMany(family, item => item.name);

// items will now contain the unique items

説明:追加された各オブジェクトから値を取得し、取得した値に基づいて、既に追加されているかどうかを判断する必要があります。値ゲッターが必要です。これはアイテムを指定する関数であり、値(item => item.name)。次に、値がまだ表示されていないアイテムのみを追加します。


クラスの実装:

// Prevents duplicate objects from being added

class ObjectSet {
  constructor(key) {
    this.key = key;
    this.items = [];
    this.set = new Set();
  }

  add(item) {
    if (this.set.has(item[this.key])) return;
    this.set.add(item[this.key]);
    this.items.Push(item);
  }

  addMany(items) {
    items.forEach(item => this.add(item));
  }
}

var mySet = new ObjectSet('name');
mySet.addMany(family);
console.log(mySet.items);
0
Dave Cousineau