二次元配列で、中の配列の特定keyに対するvalueが重複していた時に
そのvalueを持つもの全てを削除したいです。
少しコード直してみて、動くには動いたのですが
とても無駄が多いように思っているので良い方法が知りたいです。
###実現したいこと
普通の配列だとこんなイメージ。
[1,2,2,3,4,4,4] → [1,3]
// 2と4は複数あるので全部削除
今回やりたいのは、二次元かつ連想配列でこんなイメージです。
[{a:1,b:"h"},{a:1,b:"e"},{a:2,b:"l"},{a:3,b:"l"},{a:1,b:"o"}]
→ [{a:1,b:"h"},{a:1,b:"e"},{a:1,b:"o"}]
// bが"l"のものだけ削除
単純な重複削除であればfilterを使えばできるのですが
そもそも全部消したいとなると、一度重複するものを抽出して更に突合させないといけなくて
しかもそれが二次元配列かつ連想配列だと...と混乱してきてしまいました。。
↓↓一応これでできたけど、無駄がすごく多いきがする...
JavaScript
1var arr = [{a:1,b:"h"},{a:1,b:"e"},{a:2,b:"l"},{a:3,b:"l"},{a:1,b:"o"}] 2 3// 一度bだけ取り出して、重複しているものを探す 4var check = []; 5for(i=0; i<arr.length; i++){ 6 check.push(arr[i]["b"]); 7} 8var dubble = check.filter(function(x, i, arr){ 9 // check中のものが配列の中で最初にhitして、別でもhitする場合だけ抽出 10 return arr.indexOf(x) === i && i !== arr.lastIndexOf(x); 11}); 12 13var compArr = []; 14for(i=0; i<arr.length; i++){ 15 var flg = false; 16 for(j=0; j<dubble.length; j++){ 17 if(arr[i]["b"] == dubble[j]){ 18 flg = true; 19 } 20 } 21 if(!flg) compArr.push(arr[i]); 22}
###補足情報(言語/FW/ツール等のバージョンなど)
jQueryなどが使えないので、素の状態で書きたいです。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/07/03 05:27
回答4件
0
こんな感じでも出来ました。重複している値を配列化して、それらをピンポイントで削除するアプローチですね。
javascript
1var arr = [{a:1,b:"h"},{a:1,b:"e"},{a:2,b:"l"},{a:3,b:"l"},{a:1,b:"o"}]; 2 3var deleteDuplicateVal = function(array, key) { 4 var delArr = array.map(function(e, i) { 5 return e[key]; 6 }).filter(function(e, i, array) { 7 /* 重複するものを抽出 */ 8 return array.indexOf(e) !== i; 9 }).filter(function(e, i, array){ 10 /* 抽出した中での重複削除 */ 11 return array.indexOf(e) === i; 12 }); 13 14 console.log(delArr); // ["l"] 15 16 var result = array.filter(function(e, i) { 17 return delArr.indexOf(e[key]) === -1; 18 }); 19 20 return result; 21}; 22 23var filtered = deleteDuplicateVal(arr, 'b'); 24 25console.log(filtered); 26
投稿2016/07/03 14:07
編集2016/07/04 01:08総合スコア2092
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/07/03 14:38
2016/07/03 14:52 編集
2016/07/03 15:31
2016/07/04 01:14 編集
2016/07/04 02:42 編集
2016/07/04 02:38
0
findIndex + findLastIndex 型
配列を前方/後方検索し、index
が一致したら重複なしと見なします。
アルゴリズムは単純ですが、重複が全くないと配列全体を要素数だけ検索するのでコストがかかります。
また、Array.prototype.findLastIndex
が存在しないので自前定義する必要があります。
JavaScript
1/** 2 * findIndex + findLastIndex 型 3 */ 4var removeOverlapB1 = (function () { 5 function findLastIndex (array, callbackfn, thisArg) { 6 var i = array.length, hasThisArg = arguments.length > 2; 7 8 while (i--) { 9 if (hasThisArg) { 10 if (callbackfn.call(thisArg, array[i], i, array)) { 11 return i; 12 } 13 } else if (callbackfn(array[i], i, array)) { 14 return i; 15 } 16 } 17 18 return -1; 19 } 20 21 function findIndexfn (object) { 22 return object.b === this; 23 } 24 25 function filterfn (object, i, array) { 26 var b = object.b; 27 28 return array.findIndex(findIndexfn, b) === findLastIndex(array, findIndexfn, b); 29 } 30 31 return function removeOverlapB (array) { 32 return array.filter(filterfn); 33 }; 34}()); 35 36var array = [{a:1,b:"h"},{a:1,b:"e"},{a:2,b:"l"},{a:3,b:"l"},{a:1,b:"o"}] 37console.log(JSON.stringify(removeOverlapB1(array))); // [{"a":1,"b":"h"},{"a":1,"b":"e"},{"a":2,"b":"l"},{"a":1,"b":"o"}]
findIndexAll 型
Array.prototype.findIndex
を発展させ、配列全体からHITした index 値のリストを返す findIndexAll
を定義し、返り値となる配列の length
値が 0
なら重複なしと見なします。
一度、重複ありと見なしたインデックスをキャッシュし、再検索しないことで検索コストを抑えます。
ただし、配列全体が検索対象であることは変わりない為、重複が少ないと検索コストが高くなる問題は依然としてあります。
JavaScript
1/** 2 * findIndexAll 型 3 */ 4var removeOverlapB2 = (function (push) { 5 function findIndexAll (array, callbackfn, thisArg) { 6 var indexes = [], hasThisArg = arguments.length > 2; 7 8 for (var i = 0, l = array.length; i < l; ++i) { 9 if (hasThisArg) { 10 if (callbackfn.call(thisArg, array[i], i, array)) { 11 indexes.push(i); 12 } 13 } else if (callbackfn(array[i], i, array)) { 14 indexes.push(i); 15 } 16 } 17 18 return indexes; 19 } 20 21 function findIndexAllfn (object) { 22 return object.b === this; 23 } 24 25 function filterfn (object, i, array) { 26 if (this.indexOf(i) !== -1) { 27 return false; 28 } 29 30 var indexes = findIndexAll(array, findIndexAllfn, object.b); 31 32 if (indexes.length === 1) { 33 return true; 34 } 35 36 push.apply(this, indexes); 37 return false; 38 } 39 40 return function removeOverlapB (array) { 41 return array.filter(filterfn, []); 42 }; 43}(Array.prototype.push)); 44 45var array = [{a:1,b:"h"},{a:1,b:"e"},{a:2,b:"l"},{a:3,b:"l"},{a:1,b:"o"}] 46console.log(JSON.stringify(removeOverlapB2(array))); // [{"a":1,"b":"h"},{"a":1,"b":"e"},{"a":2,"b":"l"},{"a":1,"b":"o"}]
while 文 + 後方検索キャッシュ型
while
文で前方検索/後方検索し、index
が同値なら重複なしと見なします。
検索時に重複値をキャッシュする為、一度重複なしと見なされればキャッシュから重複判定出来ます。
コードが複雑化していますが、私が考えうる限りでは「検索コストが最も低い(無駄が少ない)コード」です。
JavaScript
1/** 2 * while 文 + 後方検索キャッシュ型 3 */ 4function removeOverlapB3 (array) { 5 var i = -1, 6 l = array.length, 7 j = l, 8 conflicts = [], 9 values = [], 10 results = []; 11 12 if (l < 2) { 13 return array; 14 } 15 16 while (++i < l) { 17 var current = array[i], 18 b = current.b, 19 cindex = values.indexOf(b); 20 21 if (cindex === -1) { 22 values.push(b); 23 cindex = conflicts.push(false) - 1; 24 25 while (i < --j) { 26 var target = array[j], 27 tb = target.b; 28 29 if (b === tb) { 30 conflicts[cindex] = true; 31 break; 32 } else { 33 var tindex = values.indexOf(tb); 34 35 if (values.indexOf(tb) === -1) { 36 values.push(tb); 37 conflicts.push(false); 38 } else { 39 conflicts[tindex] = true; 40 } 41 } 42 } 43 44 if (i === j) { 45 results.push(current); 46 47 while (++i < l) { 48 var current = array[i], 49 b = current.b; 50 51 if (!conflicts[values.indexOf(b)]) { 52 results.push(current); 53 } 54 } 55 56 break; 57 } 58 } else { 59 conflicts[cindex] = true; 60 } 61 } 62 63 return results; 64} 65 66var array = [{a:1,b:"h"},{a:1,b:"e"},{a:2,b:"l"},{a:3,b:"l"},{a:1,b:"o"}] 67console.log(JSON.stringify(removeOverlapB3(array))); // [{"a":1,"b":"h"},{"a":1,"b":"e"},{"a":2,"b":"l"},{"a":1,"b":"o"}]
Re: leila さん
投稿2016/07/03 08:53
編集2018/04/28 04:31総合スコア18162
0
汚い書き方ですが、指定通りに冗長にやるとこんなかんじ
javascript
1<script> 2var x=[{a:1,b:"h"},{a:1,b:"e"},{a:2,b:"l"},{a:3,b:"l"},{a:1,b:"o"},{a:3,b:"l"},{a:3,b:"l"},{a:3,b:"l"},{a:3,b:"l"}]; 3var y=[]; 4var z={}; 5for(var i=0;i<x.length;i++){ 6 if(typeof z[x[i]["b"]]=="undefined"){ 7 z[x[i]["b"]]=i; 8 y[i]=false; 9 }else{ 10 y[z[x[i]["b"]]]=true; 11 y[i]=true; 12 } 13} 14for(var i=y.length-1;i>=0;i--){ 15 if(y[i]) x.splice(i,1); 16} 17//以下検証 18for(var i=0;i<x.length;i++){ 19 console.log(i+":a="+x[i]["a"]+":b="+x[i]["b"]); 20} 21</script>
投稿2016/07/03 06:16
総合スコア114779
0
ベストアンサー
こんな感じとか。
JavaScript
1var arr = [{a:1,b:"h"},{a:1,b:"e"},{a:2,b:"l"},{a:3,b:"l"},{a:1,b:"o"}]; 2var check = []; 3arr.forEach( function ( e, i, array ) { 4 check[ e.b ] = ( e.b in check ) ? true : false; 5} ); 6var filtered = arr.filter( function ( e, i, array ) { 7 return true !== check[ e.b ]; 8} ); 9console.log( filtered );
動くサンプル:https://jsfiddle.net/d5bj2s7j/1/
追記:
JavaScript
1var arr = [{a:1,b:"h"},{a:1,b:"e"},{a:2,b:"l"},{a:3,b:"l"},{a:1,b:"o"}] ; 2var check1 = [], check2 = []; 3arr.forEach( function ( e ) { 4 if ( check1.indexOf( e.b ) !== -1 ) { check2.push( e.b ); } 5 check1.push( e.b ); 6} ); 7var filtered = arr.filter( function ( e ) { 8 return ( check2.indexOf( e.b ) === -1 ); 9} ); 10console.log( filtered );
投稿2016/07/03 05:53
編集2016/07/03 14:39総合スコア69400
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/07/03 06:27
2016/07/03 13:57 編集
2016/07/03 14:21 編集
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。