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

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

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

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

Q&A

解決済

4回答

380閲覧

オブジェクトの配列を、キーの値が同じオブジェクトでオブジェクトの配列の配列としてまとめたい

nabanaba

総合スコア13

JavaScript

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

0グッド

0クリップ

投稿2018/07/12 14:02

前提・実現したいこと

オブジェクトの配列を、キーの値が同じオブジェクトの配列の配列としてまとめるにはどうしたら良いか悩んでいます。

具体的には、コード内にあるarray_1の配列から、array_3の様な構造の配列を新規に作成したいです。

キーnameの値が同じオブジェクトをまとめて配列を作成するまではできたのですが、(array_1からarray_2の状態)
キーidの値が同じものでさらにまとめたい場合(array_2からarray_3の状態)の処理で良い方法が思いつきません。

array_1の配列から、array_3の様な構造にする為に処理を2回に分けているのですが、
他にも良いやり方があればご教示願いたいです。

javascript

1var array_1 = [ 2 { id: 0, name: "a"}, 3 { id: 2, name: "b"}, 4 { id: 3, name: "b"}, 5 { id: 3, name: "b"} 6] 7var array_2 = [ 8 [ 9 { id: 0, name: "a"} 10 ], 11 [ 12 { id: 2, name: "b"}, 13 { id: 3, name: "b"}, 14 { id: 3, name: "b"} 15 ] 16] 17var array_3 = [ 18 [ 19 { id: 0, name: "a"} 20 ], 21 [ 22 { id: 2, name: "b"} 23 ], 24 [ 25 { id: 3, name: "b"}, 26 { id: 3, name: "b"} 27 ] 28]

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

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

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

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

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

kei344

2018/07/12 14:05

ご自身で試されたコードを質問文に追記し、「何」が「どのように」わからないのか、コードのどの部分で詰まっているのかなどを具体的に追記されたほうが回答が望めると思います。
nabanaba

2018/07/13 03:59

ありがとうございます。自分でもなんとか解決できましたので、回答としました。
guest

回答4

0

こんにちは。

array_2 を経由せずに、array_1 から直接array_3を得る方法として、
以下を考えました。

javascript

1var array_3 = array_1.reduce(function(a, e) { 2 a.some(x => x[0].id == e.id && x[0].name == e.name && x.push(e)) || a.push([e]); 3 return a; 4}, []);

以下にて、動作確認しています。

https://jsfiddle.net/jun68ykt/Lfy3odqu/42/

参考になれば幸いです。


補足

上記のコード中、以下の行

a.some(x => x[0].id == e.id && x[0].name == e.name && x.push(e)) || a.push([e]);

について、以下の2点を補足します。

(1) x.push(e) は、eが追加された後のxの長さを返すので、x.push(e) の返す値は常に1以上の整数であり、1以上の整数を論理値として評価すると trueになります。これを some メソッドから true を返させるために使っています。

(2) x[0]e との比較で、プロパティ idnameともに等しいかを検証する前に、念のため先頭に x が空の配列ではない検証を追加して、

x.length > 0 && x[0].id == e.id && ・・・

しなくてもよい 理由は、a に要素が追加されるのは、

a.push([e])

のときしかないので、a の要素である配列 x は空ではなく、
少なくとも1個の要素を持つことが分かっているからです。

投稿2018/07/12 21:20

編集2018/07/13 01:35
jun68ykt

総合スコア9058

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

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

nabanaba

2018/07/13 04:16

自分にはちょっと難しくて、someメソッド内の処理を理解するのに時間がかかってしまいました。 reduceメソッドも使える様に勉強したいです。
jun68ykt

2018/07/13 04:32

reduce は慣れると色々な場面で使えて便利です。 参考までに、以下 https://jsfiddle.net/jun68ykt/52dmp7x3/7/ は miyabi-sunさんのご回答に出てくる tmp を作るために reduce を使ってみた回答です。
nabanaba

2018/07/13 05:20

ありがとうございます。 MDN で調べたら他にも色々できそうなのでちゃんと読んでおこうと思います。
guest

0

ベストアンサー

2次元配列にした時点でかなり触りづらいですね。
なので質問文のケースではarray_1から2を経由するのではなく、
直接array_3に加工した方が早いです。

Object.values()を使えば一発解決するんじゃないですかね?

JavaScript

1var array_1 = [ 2 { id: 0, name: "a"}, 3 { id: 2, name: "b"}, 4 { id: 3, name: "b"}, 5 { id: 3, name: "b"} 6] 7var tmp = {}; // 空のオブジェクトを宣言しておく 8for (var it of array_1) { 9 var key = `${it.id}-${it.name}`; 10 if (tmp[key]) { 11 tmp[key].push(it); 12 } else { 13 tmp[key] = [it]; 14 } 15} 16 17console.log(tmp); 18console.log(Object.values(tmp)); 19// この中身は実際に動作して確認してみてね

投稿2018/07/12 14:34

miyabi-sun

総合スコア21158

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

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

nabanaba

2018/07/13 04:14

Object.values()を使うのを思いつかなかったです。 わかりやすさを優先してベストアンサーとさせていただきました。
guest

0

すでに回答いただいておりますが、
自分でもなんとか下記の様に解決することができました。
比べるとすごい冗長、、、

javascript

1var array_1 = [ 2 { id: 0, name: "a"}, 3 { id: 2, name: "b"}, 4 { id: 3, name: "b"}, 5 { id: 3, name: "b"} 6] 7console.log("array_1", array_1); 8 9var key = "name" 10var array_2 = []; 11 12array_2 = getMargeArr(array_1, "name"); 13console.log("array_2", array_2); 14 15var array_3 = []; 16 17for (var i in array_2) { 18 var subArr = []; 19 subArr = getMargeArr(array_2[i], "id"); 20 Array.prototype.push.apply(array_3, subArr); 21} 22console.log(array_3); 23 24 25function getMargeArr(arr, key){ 26 var newArr = []; 27 for (var i in arr) { 28 if(typeof arr[i][key] !== undefined){ 29 var target_val = arr[i][key]; 30 var searchArr = searchArray(newArr, target_val, key) 31 32 if (searchArr === undefined) { 33 searchArr = [] 34 newArr.push(searchArr) 35 } 36 searchArr.push(arr[i]) 37 } 38 } 39 return newArr 40} 41 42function searchArray(list, target_val, key) { 43 if(list.length !== 0){ 44 for (var i in list) { 45 if (list[i][0][key] === target_val) { 46 return list[i] 47 } 48 } 49 } 50 return undefined 51}

投稿2018/07/13 03:58

nabanaba

総合スコア13

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

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

0

問題点

JavaScript

1var array_3 = [ 2 [ 3 { id: 0, name: "a"} 4 ], 5 [ 6 { id: 2, name: "b"} 7 ], 8 [ 9 { id: 3, name: "b"}, 10 { id: 3, name: "b"} 11 ] 12]

期待する配列に問題があるように感じます。

(問題1) 特定の id を持つオブジェクト群を探す為に全要素を検索しなければならない
(問題2) id の若い順番でソートすることまで期待しているのか不明

(問題1) については要求仕様の問題でせっかくid別に分類したのに、特定idを探し出す為に余計なコストがかかってしまいます。
(問題2) については既存の全回答で No ですが、array_1 が若番順でソートされているので、質問者がその問題に気が付いてない懸念があります。

new Map (定義順)

(問題1) は new Map で解決できます。
ただし、Map は定義順で iterable なオブジェクトを生成するので、愚直に set すると id の若番順で並びません。

JavaScript

1'use strict'; 2function toMap (array) { 3 return array.reduce(function (map, current) { 4 var id = current.id; 5 6 delete current.id; 7 map.has(id) ? map.get(id).push(current) : map.set(id, [current]); 8 9 return map; 10 }, new Map); 11} 12 13var array_1 = [{id: 0, name: "a"}, {id: 2, name: "b"}, { id: 3, name: "b"}, {id: 3, name: "b"}], 14 array_2 = [{id: 3, name: "b"}, {id: 3, name: "b"}, {id: 2, name: "b"}, {id: 0, name: "b"}]; 15 16 17console.log(JSON.stringify([...toMap(array_1)])); // [[0,[{"name":"a"}]],[2,[{"name":"b"}]],[3,[{"name":"b"},{"name":"b"}]]] 18console.log(JSON.stringify([...toMap(array_2)])); // [[3,[{"name":"b"},{"name":"b"}]],[2,[{"name":"b"}]],[0,[{"name":"b"}]]]

配列 (若番順)

(問題2) への対策として、配列で解決する方法があります。
配列は定義順ではなく、若番順で列挙する事が期待できます。

ただし、id が配列のプロパティ名に依存するので、id を String 型に変換してしまいます
id の型を維持するのであれば、new Map が妥当です。

JavaScript

1'use strict'; 2function toArray (array) { 3 return array.reduce(function (array, current) { 4 var id = current.id; 5 6 delete current.id; 7 id in array ? array[id].push(current) : array[id] = [current]; 8 9 return array; 10 }, []); 11} 12 13var array_1 = [{id: 0, name: "a"}, {id: 2, name: "b"}, { id: 3, name: "b"}, {id: 3, name: "b"}], 14 array_2 = [{id: 3, name: "b"}, {id: 3, name: "b"}, {id: 2, name: "b"}, {id: 0, name: "b"}]; 15 16console.log(toArray(array_1)); // [[{"name":"a"}],,[{"name":"b"}],[{"name":"b"},{"name":"b"}]] 17console.log(toArray(array_2)); // [[{"name":"a"}],,[{"name":"b"}],[{"name":"b"},{"name":"b"}]]

new Map (若番順)

new Map を呼び出す前にソートすれば、id の若番順で並びます。

JavaScript

1function toMap (array) { 2 return new Map(array.reduce(function (array, current) { 3 const id = current.id; 4 5 delete current.id; 6 7 for (let element of array) { 8 if (element[0] === id) { 9 element[1].push(current); 10 return array; 11 } 12 } 13 14 array.push([id, [current]]); 15 return array; 16 }, []).sort(function (a, b) { 17 return a > b; 18 })); 19} 20 21var array_1 = [{id: 0, name: "a"}, {id: 2, name: "b"}, { id: 3, name: "b"}, {id: 3, name: "b"}], 22 array_2 = [{id: 3, name: "b"}, {id: 3, name: "b"}, {id: 2, name: "b"}, {id: 0, name: "b"}]; 23 24console.log(JSON.stringify([...toMap(array_1)])); // [[0,[{"name":"a"}],[2,[{"name":"b"}]],[3,[{"name":"b"},{"name":"b"}]]] 25console.log(JSON.stringify([...toMap(array_2)])); // [[0,[{"name":"a"}]],[2,[{"name":"b"}]],[3,[{"name":"b"},{"name":"b"}]]]

Re: nabanaba さん

投稿2018/07/14 09:01

think49

総合スコア18166

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

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

nabanaba

2018/07/17 03:00 編集

回答ありがとうございます。 問題2については、事前に別のキーでソートしていたので問題ないかと思っていましたが、考えてみると必要ですね、、、 一度ソートしてから処理したいと思います。 id: 0 がなくなるのは後の処理で使うので困るのと、下記のパターンもあるので、キーと値のセットで持ちたいと思っていました。 ```javascript var array_3 = [ [ { id: 0, name: "a"} ], [ { id: 2, name: "b"} ], [ { id: 3, name: "b"}, { id: 3, name: "b"} ], [ { id: 3 , name: "c"}, { id: 3, name: "c"} ] ] ```
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.47%

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

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

質問する

関連した質問