質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

4回答

253閲覧

重複のないhashデータをフィルターする方法

nanase21

総合スコア144

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2020/08/27 17:57

下記のようなデータをフィルターする方法を知りたい

js

1// フィルターしたいデータ 2// const items = 3// [{id: 1, name: 'apple', course_id: 1}, 4// {id: 2, name: 'orange', course_id: 1}, 5// {id: 3, name: 'peach', course_id: 1}, 6// {id: 1, name: 'apple', course_id: 1}, 7// {id: 1, name: 'lemon', course_id: 2}] 8 9// 求めている答え 10// [{id: 1, name: 'apple', course_id: 1}, 11// {id: 2, name: 'orange', course_id: 1}, 12// {id: 3, name: 'peach', course_id: 1}, 13// {id: 1, name: 'lemon', course_id: 2}] 14 15// 試したコード 16const items = 17 [{id: 1, name: 'apple', course_id: 1}, 18 {id: 2, name: 'orange', course_id: 1}, 19 {id: 3, name: 'peach', course_id: 1}, 20 {id: 1, name: 'apple', course_id: 1}, 21 {id: 1, name: 'lemon', course_id: 2}]; 22let target = []; 23let hoge; 24for(let i=0;i<items.length;i++){ 25 if(target.length == 0){ 26 target.push(items[i]) 27 }else{ 28 hoge = items[i] 29 for(let x=0;x<target.length;i++){ 30 if(target[x].id != hoge.id && target[x].course_id != hoge.course_id){ 31 target.push(hoge) 32 } 33 } 34 } 35} 36console.log(target) 37// としたが動きませんでした...

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

こんにちは

ご質問の題意としては、

  • 配列items の要素のオブジェクトについて、プロパティのすべてが同じ値であるものが二個以上含まれないように、かつ、要素の順番は保存されている配列を target に得たい。

ということと解釈しました。(この解釈が違っていたら、コメントからお知らせください。)このようなtarget は、以下によって得られます。

Set を使用

重複をチェックするために Set を使います。

各オブジェクトの name は、アルファベットのみから構成される文字列であると仮定とすると、各オブジェクトの等値性は、id, name, course_id の順に値を連結して得られる文字列を比較すればよいことになります。この文字列を各要素の**値(value)**と呼ぶことにすると、items に出現する各要素の値を、Set に格納していけば、重複チェックの計算量をO(1)で済ませることができます。

javascript

1const set = new Set(); 2 3const target = items.filter(e => { 4 const value = `${e.id}${e.name}${e.course_id}`; 5 if (set.has(value)) { 6 return false; 7 } else { 8 set.add(value); 9 return true; 10 } 11});

lodash を使用

配列やオブジェクトの操作で便利なライブラリ lodash を使うと以下のように手短かに書けます。

javascript

1const target = _.uniqWith(items, _.isEqual);

ただし、上記はオブジェクトの等値判定に、汎用的な _.isEqualを使っているところが非効率です。先と同様にオブジェクトの値(value)を定義できるのであれば、オブジェクトの値を取得するメソッドvalueOfを作っておいて、等値判定のためにこれを利用できます。以下はその例です。

javascript

1const valueOf = e => `${e.id}${e.name}${e.course_id}`; 2 3const target = _.uniqBy(items, valueOf);

なお、name プロパティに、'9apple''lemon3' といったような、数字で始まる、または数字で終わる文字列が出現する可能性があるならば、オブジェクトの等値性を判定する値として、

javascript

1`${e.id}${e.name}${e.course_id}`

は使えないので、オブジェクトの値(value)の別の作り方を検討する必要があります。このご質問のような課題の解決の際に、各オブジェクトの等値性を判定するための値(言い換えると、各オブジェクトを何らかのクラスのインタンスとして得ることにした場合に、そのクラスのvalueOfメソッドが返す値)をうまく決められるかどうか、というのがひとつの検討事項になり得ます。

以上、参考になれば幸いです。

追記

はじめに挙げた、Set を使うコードのほうで、Setのインスタンスをnewで作るコストが気になるようであれば、Setの替わりにプレーンオブジェクトを使うこともできます。

投稿2020/08/27 21:25

編集2020/08/28 01:00
jun68ykt

総合スコア9058

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nanase21

2020/08/28 02:05

様々な、方法をご教示いただきありがとうございます。 参考にさせて頂きます。
guest

0

前回の拡張

javascript

1const res=items.reduce((x,y)=>(!x.map(z=>z.id+"_"+z.course_id).includes(y.id+"_"+y.course_id)&&x.push(y),x),[]);

投稿2020/08/28 00:26

yambejp

総合スコア114843

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nanase21

2020/08/28 02:03

ご教示いただきありがとうございます。 コードの処理の理解が追いつきませんが、参考にさせて頂きたいと思います。
yambejp

2020/08/28 02:04

前回とほぼ一緒のコードですよね? しかもロジックは前回質問者さんが提示されたものとほぼ一緒です
nanase21

2020/08/28 02:08

前回は、course_idを起点として、そのデータの中の最初のデータを取得を目的として質問させて頂きました。 今回の質問では、ダブりのあるデータのみを除外する目的で質問させて頂きました。 ちょっと考えれば、前回のコードを応用してもできそうな感じですが、私の力不足で再度、似たような質問をさせて頂きました。
guest

0

ベストアンサー

もっとパフォーマンスのよい

ご自身の filter を用いた手法で十分なパフォーマンスを得られています。


Setと比較してみました(開発者コンソールで複数回実行するため、var 宣言にしています)。

javascript

1// 試したコード 2var items = 3 [{id: 1, name: 'apple', course_id: 1}, 4 {id: 2, name: 'orange', course_id: 1}, 5 {id: 3, name: 'peach', course_id: 1}, 6 {id: 1, name: 'apple', course_id: 1}, 7 {id: 1, name: 'lemon', course_id: 2}]; 8 9console.time("case1"); 10for(let i=0;i<100000; ++i) { 11 const result = items.filter((element, index, self) => 12 self.findIndex(e => 13 e.id == element.id && 14 e.name == element.name 15 ) === index); 16} 17console.timeEnd("case1"); 18 19console.time("case2") 20for(let i=0;i<100000; ++i) { 21 var rslt = [...(new Set(items)).values()]; 22} 23console.timeEnd("case2");
  • RaspberryPi 3B+
  • Chromium バージョン: 72.0.3626.121(Official Build)Built on Raspbian , running on Raspbian 9.11 (32 ビット)

にて、ざっくり、以下の結果を確認しました。

label1st2nd3rd
case1:196.70162.02189.81
case2:614.45610.00635.04

※新しいSetオブジェクトを生成するコストが差になっているのかもしれません。

投稿2020/08/27 23:12

AkitoshiManabe

総合スコア5432

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nanase21

2020/08/28 02:05

ご教示いただきありがとうございます。 パフォーマンスの計測などやったことがなかったので勉強になりました。
guest

0

js

1const result = items.filter((element, index, self) => 2 self.findIndex(e => 3   e.id == element.id && 4 e.name == element.name 5 ) === index);

これで一応、求めている答えにはなりました。

投稿2020/08/27 18:09

nanase21

総合スコア144

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nanase21

2020/08/27 18:09

他に、もっとパフォーマンスのよい書き方があればご教示いただけると嬉しいです。
AkitoshiManabe

2020/08/27 23:13

十分なパフォーマンスだと思います。本題とは関係ありませんが、console.time() / console.timeEnd() を使ったチェック方法を回答させていただきました。
nanase21

2020/08/28 02:02

コメントありがとうございます。 ご教示していただいた方法について勉強させていただきたいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問