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

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

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

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

Q&A

解決済

4回答

17909閲覧

二次元配列で、特定Keyに対するValueが重複しているものを、1つ残して重複を無くすのではなく全部削除したい

leila

総合スコア13

JavaScript

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

1グッド

1クリップ

投稿2016/07/03 04:58

編集2016/07/03 05:45

二次元配列で、中の配列の特定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などが使えないので、素の状態で書きたいです。

miyabi-sun👍を押しています

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

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

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

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

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

kei344

2016/07/03 05:01

「一度重複するものを抽出して更に突合わせた」場合のコードを未完成でも良いので追記されたほうが回答が得やすいと思います。
leila

2016/07/03 05:27

ありがとうございます、追記してみました!
guest

回答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
yamato_hikawa

総合スコア2092

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

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

think49

2016/07/03 14:38

> delete e[key]; 削除対象はプロパティbではなく、該当要素自身ではないでしょうか。 > var result = array.map(function(e, i) { おそらく、Array#mapは配列のディープコピー目的で使われたと思いますが、実際にはシャローコピーなので、破壊的な変更が加えられています。 console.log(JSON.stringify(arr)); // [{"a":1,"b":"h"},{"a":1,"b":"e"},{"a":2},{"a":3},{"a":1,"b":"o"}] console.log(JSON.stringify(filtered)); // [{"a":1,"b":"h"},{"a":1,"b":"e"},{"a":2},{"a":3},{"a":1,"b":"o"}] 下記コードに修正して期待通りに動作する事を確認しました。 var result = array.filter(function(e, i) { return delArr.indexOf(e[key]) === -1; });
yamato_hikawa

2016/07/03 14:52 編集

補足ありがとうございます。要件を勘違いしていました。 delArrにないものだけを返すということですね。コードを修正しました。
leila

2016/07/03 15:31

ご回答ありがとうございました!そして質問解りづらくてすみません...!!>< 重複している値を配列化してピンポイント削除、の考え方は 最初、そんな流れで書けないかな?と思っていた形だったのでとてもしっくり来ました。
yamato_hikawa

2016/07/04 01:14 編集

{b:"l"}が存在するオブジェクトが3つ以上あるときに、delArrの配列が["l","l"...]となってしまっていたので、重複削除のロジックを追加しておきました。 ただし、delArrの要素が重複していても最終結果自体は変わりません。
think49

2016/07/04 02:42 編集

> 重複削除のロジックを追加しておきました。 参考までに、reduceを使えば「filter x 2 -> reduce x 1」に抑えられますね。 https://jsfiddle.net/0pfmffke/ また、逆転の発想で lastIndexOf で「重複しない要素」をfilterするようにしてやれば、検索しながら「重複する最後の要素のindex値」をキャッシュしてやれば済むようになり、検索効率が上がります。 https://jsfiddle.net/0pfmffke/2/
yamato_hikawa

2016/07/04 02:38

その辺りはいろいろとチューニングできそうですね。ありがとうございます。
guest

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
think49

総合スコア18162

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

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

leila

2016/07/03 15:24

3パターンもご丁寧に、しかもコスト面までご説明ありがとうございます! 恥ずかしながらarguments知りませんでした... 1つ1つお手本にさせていただきます。
guest

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

yambejp

総合スコア114779

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

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

leila

2016/07/03 06:42

最初のindexの位置を利用してtrue/false判断を全体でしていくのですね。 削除していく、の形でいただきありがとうございますー!
guest

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 );

動くサンプル:https://jsfiddle.net/d5bj2s7j/2/

投稿2016/07/03 05:53

編集2016/07/03 14:39
kei344

総合スコア69400

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

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

leila

2016/07/03 06:27

すごい解りやすいですー!! 無理に重複のものだけとかじゃなくて、判断できる材料をちゃんと揃えれば 綺麗にできるのですね...ありがとうございます!
think49

2016/07/03 13:57 編集

このコードには次の問題があります。  (問題点1) in 演算子はプロトタイプチェーンを辿る(Object.prototype.valueOf や Array#length を存在すると認識する)  (問題点2) プロパティに b 値を埋め込む関係上、プロパティ b 値が String 型に変換される。つまり、{} === {} や '1' === 1 と判定される。 従って、重複が存在しない次の配列を与えても [] を返してきます。 var arr = [{a: 1, b: 'valueOf'}, {a: 1, b: {}}, {a: 2, b: {}}, {a: 3,b: 1}, {a: 1, b: '1'}]; > var check = []; ここは var check = {}; と書きたかったのでしょうか。 配列を使うなら in 演算子ではなく、Array#indexOf を使う場面だと思います。 オブジェクト初期化子を使うなら Object.create(null) を使えば [[Prototype]] が空になります(ただし、String 型に変換される問題は残ります)。 あるいは、new Map も良いと思います。
kei344

2016/07/03 14:21 編集

To: think49 > 問題点 おっしゃるとおりです。まず、プロパティ b 値が String 型以外を想定していませんでした。in 演算子の挙動も頭から抜けていました。 > var check = []; 例示されたものをそのまま考えずに使った、というだけです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問