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

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

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

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

jQuery

jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

Q&A

解決済

4回答

2957閲覧

JavaScriptの配列で重複しているものを除外したい

agepan

総合スコア66

JavaScript

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

jQuery

jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

0グッド

0クリップ

投稿2017/09/14 12:31

JavaScriptの配列に関する質問です。
配列の重複を除去する方法を調べております。こちらのページでは重複を削除して(2つあるものを1つにする)重複のない配列を作成する方法が書かれておりますが、重複している項目を完全に除去した配列を作成する方法を模索しております(=重複していない項目だけを残した配列を作成したい)。

具体的には以下の配列があるときに、重複している2,3を全て除去して、1と5のみ抽出した配列を作成したいと思うのですが、どのようなコードで実現できますでしょうか?

よろしくお願いいたします。

var a = [1,2,3,3,2,2,5]; // result = [1,5] ←上記の時、結果がこの配列になるようなコードを探しております

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

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

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

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

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

guest

回答4

0

下記ではいかがでしょうか。
リンク頂いたページの「重複のみをリスト」の逆の出力とすれば求める結果となります。

Javascript

1var a = [1,2,3,3,2,2,5]; 2 3// 重複していないものをリスト 4var e = a.filter(function (x, i, self) { 5 return self.indexOf(x) === self.lastIndexOf(x); 6}); 7 8console.log(e)

投稿2017/09/14 12:48

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

agepan

2017/09/16 02:14

ご回答ありがとうございました! 最初にこちらのコードでテストいたしまして、問題無く動きました。 また、今回の質問の発端となった問題点について、別途質問として投稿いたしました。もしお時間ありましたらこちらも目を通して頂けますと幸いです。 https://teratail.com/questions/92683
guest

0

ベストアンサー

indexOf, lastIndexOf は NaN 値を検索できない

Array#indexOf, Array#lastIndexOf には「NaN 値を検索できない」という欠点があります。
ES7 ではこの問題に対応する為に Array.prototype.includes が定義されました。

JavaScript

1console.log([NaN].indexOf(NaN)); // -1 2console.log([NaN].lastIndexOf(NaN)); // -1 3console.log([NaN].includes(NaN)); // true

試行回数の問題

suyamaさんのコードにはもう一つ問題があります。

JavaScript

1var e = a.filter(function (x, i, self) { 2 return self.indexOf(x) === self.lastIndexOf(x); 3});

このコードは1回検査する毎に「配列全体を検索」しています。
従って、配列の長さが長い場合は検索に時間がかかってしまいます。
要素に NaN が含まれていた場合は、indexOf, lastIndexOf でそれぞれ配列全体を検索する為、さらに時間がかかります。

ベンチマーク

試行回数を極限まで減らしたアルゴリズムでコードを書き、ベンチマークをとってみました。

JavaScript

1'use strict'; 2function think49 (array) { 3 var i = 0, len = array.length, value, map = new Map, unique = []; 4 5 while (i++ < len) { 6 value = array[i]; 7 map.set(value, map.has(value) ? map.get(value) + 1 : 1); 8 } 9 10 for (var entry of map.entries()) { 11 if (entry[1] === 1) { 12 unique.push(entry[0]); 13 } 14 } 15 16 return unique; 17} 18 19var suyama = (function () { 20 function filterfn (x, i, self) { 21 return self.indexOf(x) === self.lastIndexOf(x); 22 } 23 24 return function suyama (array) { 25 return array.filter(filterfn); 26 }; 27}()); 28 29function shuffle (array) { 30 var k, t, len; 31 32 len = array.length; 33 34 if (len < 2) { 35 return array; 36 } 37 38 while (len) { 39 k = Math.floor(Math.random() * len--); 40 t = array[k]; 41 array[k] = array[len]; 42 array[len] = t; 43 } 44 45 return array; 46}; 47 48function createArray (uniqueLength, duplicateLength, NaNLength) { 49 uniqueLength = arguments.length > 0 ? uniqueLength : 0; 50 duplicateLength = arguments.length > 1 ? duplicateLength : 0; 51 NaNLength = arguments.length > 2 ? NaNLength : 0; 52 53 var array = [...Array(uniqueLength + duplicateLength).keys()], 54 duplicateArray = array.slice(uniqueLength); 55 56 return array.slice(0, uniqueLength).concat(shuffle(duplicateArray.concat(duplicateArray).concat(Array(NaNLength).fill(NaN)))); 57} 58 59function benchmark (fn, createArg, i) { 60 console.time(fn.name); 61 62 while (i--) { 63 fn(createArg()); 64 } 65 66 console.timeEnd(fn.name); 67} 68 69benchmark(think49, createArray.bind(null, 100, 20000, 100), 10); 70benchmark(think49, createArray.bind(null, 20000), 10); 71benchmark(think49, createArray.bind(null, 0, 0, 20000), 10); 72 73benchmark(suyama, createArray.bind(null, 100, 20000, 100), 10); 74benchmark(suyama, createArray.bind(null, 20000), 10); 75benchmark(suyama, createArray.bind(null, 0, 0, 20000), 10);

結果は次の通りです。

引数に指定した配列コードコード実行時間
非重複要素100個 + 重複要素20000個 + NaN値の要素10個think49228.81396484375ms
非重複要素100個 + 重複要素20000個 + NaN値の要素10個suyama19696.505859375ms
非重複要素20000個think4981.341064453125ms
非重複要素20000個suyama7361.458984375ms
NaN値の要素20000個think4941.86279296875ms
NaN値の要素20000個suyama7258.05810546875ms

最後の「NaN値の要素20000個」は意地悪問題でした。
NaN値は重複していますが、前述の通り、indexOf, lastIndexOf は NaN 値を検出できない為、配列全体を検索する過程を 2 * 20000 回行います。
(正確には一つ重複要素がある為、20001 個の要素を持つ配列を検索しているのと等価です。)

Re: agepan さん

投稿2017/09/15 13:08

編集2017/09/15 13:12
think49

総合スコア18162

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

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

agepan

2017/09/16 02:10 編集

ご回答ありがとうございました! とても詳しく検証までしていただきありがたく思います。様々な方法があるようですので、今回の要件に合いそうな物を選び実装させていただきたいと思います。 また、今回の質問の発端となった問題点について、別途質問として投稿いたしました。もしお時間ありましたらこちらも目を通して頂けますと幸いです。 https://teratail.com/questions/92683 ありがとうございました!
guest

0

IEはかなぐり捨ててます。

JavaScript

1const a = [1, 2, 3, 3, 2, 2, 5]; 2const result = Array.from(a).sort().concat(Symbol()).reduce( 3 (obj, v) => ((same) => ({ 4 pre: v, 5 count: same ? obj.count + 1 : 1, 6 list: (!same && obj.count === 1) ? obj.list.concat(obj.pre) : obj.list, 7 }))(obj.pre === v || (Number.isNaN(obj.pre) && Number.isNaN(v))), 8 {pre: Symbol(), count: 0, list: []}).list; 9console.log(result);

sort()はたぶんO(n log n)だろうけど、ネイティブな実装なので高速と仮定すれば、それ以外はたぶんO(n)なので巨大な配列でもそこそこ速く行けるかも知れない。(concat()ってO(1)なのだろうかという疑問はあるけど)

JavaScript

1const a = [1, 2, 3, 3, 2, 2, 5]; 2const result = new Set; 3const looked = new Set; 4for (const v of a) { 5 if (looked.has(v)) { 6 result.delete(v); 7 } else { 8 result.add(v); 9 looked.add(v); 10 } 11} 12console.log(Array.from(result));

Setの実装がすごくよくできたハッシュテーブルであれば各メソッドは約O(1)になるはず。そうなると、正真正銘のO(n)となるかと思います。

投稿2017/09/15 11:20

編集2017/09/15 14:32
raccy

総合スコア21735

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

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

think49

2017/09/15 13:59 編集

上のコードは速く動くイメージが出てこなくて、ベンチマークを取ってみたら速くありませんでした。 https://jsfiddle.net/81m6t12p/ 出力値の配列の順序が変わってしまうのは、人に依っては気にするかもしれません。 (重複チェックの機構としては問題ないですが、[1,2,3,10,100].sort() が [1, 10, 100, 2, 3] になるのは期待通りでしょうか) 下のコードは安定して速く、特に重複が多いと高速ですね。 (ケアレスミスだと思いますが、v で ReferenceError が返ってきます。)
raccy

2017/09/15 14:36 編集

あ、constを付け忘れてますね。paiza.ioだとエラーにならなかったので気付かなかったです。 上のコードは全てimmutableにしているため、オブジェクト生成コストがかかるので、実装次第ではたぶん遅いです。JavaScriptで関数型プログラミングはやっぱりやりにくいです。
agepan

2017/09/16 02:12

回答ありがとうございました! こちらのコードでも実装テストを現在行っております(正しく動作しているようです)。また、今回の質問の発端となった問題点について、別途質問として投稿いたしました。もしお時間ありましたらこちらも目を通して頂けますと幸いです。 https://teratail.com/questions/92683
guest

0

要件を誤読していたため、下記方法は要件を満たしません。コメント欄にてthink49さんmiyabi-sunさんがreduceを利用する方法を書いていただいています。


reduceを使う方法。

JavaScript

1let arr = [ 3, 4, 10, 4, 3, 2, 4, 3, 1, 6, 2, 7, 4, 7, 1 ].reduce( ( prev, curr ) => { 2 if ( !prev.includes( curr ) ) { 3 prev.push( curr ); 4 } 5 return prev; 6}, [] ); 7console.log( arr );

【Array.prototype.reduce() - JavaScript | MDN】
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce

【Array.prototype.includes() - JavaScript | MDN】
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/includes

投稿2017/09/14 18:50

編集2017/09/15 03:20
kei344

総合スコア69400

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

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

think49

2017/09/14 22:53

要件を誤解していると思います。 > let arr = [ 3, 4, 10, 4, 3, 2, 4, 3, 1, 6, 2, 7, 4, 7, 1 ] この配列に対して、[10, 6] が返ってくることを質問者さんは期待しています。 reduce を使うのなら、「重複リスト」と「非重複リスト」を作って2回チェックする必要があると思われます(bindを併用するか、スコープチェーン上にもう一つ変数が必要)。
kei344

2017/09/15 02:10

To: think49さん 指摘ありがとうございます。質問文を読めていませんでした・・・。
miyabi-sun

2017/09/15 03:12 編集

```JavaScript let is_uniq_in = arr => it => arr.reduce((a, that) => it === that ? a + 1 : a, 0) === 1 let arr = [ 3, 4, 10, 4, 3, 2, 4, 3, 1, 6, 2, 7, 4, 7, 1 ] console.log(arr.filter(is_uniq_in(arr))) // [10, 6] ``` reduceだとこれが限界かも? ライブラリなしだとすやまさんアプローチが最善っぽいですね。
kei344

2017/09/15 03:22

To: miyabi-sunさん > ライブラリなしだとすやまさんアプローチが最善っぽいですね。 その通りだと思います。 誤読した上で「(特に意味はないけど)reduceでも書けそう」と考えて回答しました・・・。
think49

2017/09/15 09:33

suyamaさんの方法は - NaN値をチェック出来ない - 重複のない大きなlength値を持つ配列においての処理が遅い(100の要素があれば、100 * 200 回テストする) という欠点がある為、必ずしも最善ではないと思います。 reduce は上手く使えば、試行回数を減らす事が出来ます。
miyabi-sun

2017/09/15 10:10

think49さん なるほどー、groupBy→keysの方向でやれば*2n程度の計算量に収まりそうですね。
think49

2017/09/15 13:22

To: miyabi-sun さん > groupBy→keysの方向でやれば*2n程度の計算量に収まりそうですね。 groupBy は lodash の関数でしょうか。 いろいろ考えましたが、最終的には *2n の範囲におさまりました。 *1n を目指したかったのですが、上手い方法が思い浮かびませんでした。
agepan

2017/09/16 02:13

回答ありがとうございました! こちらの内容も、別の課題で役に立ちそうな有益な情報でありましたのでありがたく参考にさせて頂きます! また、今回の質問の発端となった問題点について、別途質問として投稿いたしました。もしお時間ありましたらこちらも目を通して頂けますと幸いです。 https://teratail.com/questions/92683
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問