JavaScriptの配列に関する質問です。
配列の重複を除去する方法を調べております。こちらのページでは重複を削除して(2つあるものを1つにする)重複のない配列を作成する方法が書かれておりますが、重複している項目を完全に除去した配列を作成する方法を模索しております(=重複していない項目だけを残した配列を作成したい)。
具体的には以下の配列があるときに、重複している2,3を全て除去して、1と5のみ抽出した配列を作成したいと思うのですが、どのようなコードで実現できますでしょうか?
よろしくお願いいたします。
var a = [1,2,3,3,2,2,5]; // result = [1,5] ←上記の時、結果がこの配列になるようなコードを探しております
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答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
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個 | think49 | 228.81396484375ms |
非重複要素100個 + 重複要素20000個 + NaN値の要素10個 | suyama | 19696.505859375ms |
非重複要素20000個 | think49 | 81.341064453125ms |
非重複要素20000個 | suyama | 7361.458984375ms |
NaN値の要素20000個 | think49 | 41.86279296875ms |
NaN値の要素20000個 | suyama | 7258.05810546875ms |
最後の「NaN値の要素20000個」は意地悪問題でした。
NaN値は重複していますが、前述の通り、indexOf, lastIndexOf は NaN 値を検出できない為、配列全体を検索する過程を 2 * 20000 回行います。
(正確には一つ重複要素がある為、20001 個の要素を持つ配列を検索しているのと等価です。)
Re: agepan さん
投稿2017/09/15 13:08
編集2017/09/15 13:12総合スコア18162
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
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総合スコア21735
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/09/15 13:59 編集
2017/09/15 14:36 編集
2017/09/16 02:12
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総合スコア69400
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/09/14 22:53
2017/09/14 23:02
2017/09/15 02:10
2017/09/15 03:12 編集
2017/09/15 03:22
2017/09/15 09:33
2017/09/15 10:10
2017/09/15 13:22
2017/09/16 02:13
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/09/16 02:14