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

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

ただいまの
回答率

90.53%

  • JavaScript

    16320questions

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

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

解決済

回答 4

投稿

  • 評価
  • クリップ 0
  • VIEW 247

nabanaba

score 5

 前提・実現したいこと

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

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

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

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

var array_1 = [
    { id: 0, name: "a"},
    { id: 2, name: "b"},
    { id: 3, name: "b"},
    { id: 3, name: "b"}
]
var array_2 =  [
  [
    { id: 0, name: "a"}
  ],
  [
    { id: 2, name: "b"},
    { id: 3, name: "b"},
    { id: 3, name: "b"}
  ]
]
var array_3 = [
  [
    { id: 0, name: "a"}
  ],
  [
    { id: 2, name: "b"}
  ],
  [
    { id: 3, name: "b"},
    { id: 3, name: "b"}
  ]
]
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • kei344

    2018/07/12 23:05

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

    キャンセル

  • nabanaba

    2018/07/13 12:59

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

    キャンセル

回答 4

checkベストアンサー

+3

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

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

var array_1 = [
    { id: 0, name: "a"},
    { id: 2, name: "b"},
    { id: 3, name: "b"},
    { id: 3, name: "b"}
]
var tmp = {}; // 空のオブジェクトを宣言しておく
for (var it of array_1) {
  var key = `${it.id}-${it.name}`;
  if (tmp[key]) {
    tmp[key].push(it);
  } else {
    tmp[key] = [it];
  }
}

console.log(tmp);
console.log(Object.values(tmp));
// この中身は実際に動作して確認してみてね

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/07/13 13:14

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

    キャンセル

+3

こんにちは。

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

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

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

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/13 13:16

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

    キャンセル

  • 2018/07/13 13:32

    reduce は慣れると色々な場面で使えて便利です。

    参考までに、以下

    https://jsfiddle.net/jun68ykt/52dmp7x3/7/

    は miyabi-sunさんのご回答に出てくる tmp を作るために reduce を使ってみた回答です。

    キャンセル

  • 2018/07/13 14:20

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

    キャンセル

+2

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

var array_1 = [
    { id: 0, name: "a"},
    { id: 2, name: "b"},
    { id: 3, name: "b"},
    { id: 3, name: "b"}
]
console.log("array_1", array_1);

var key = "name"
var array_2 = [];

array_2 = getMargeArr(array_1, "name");
console.log("array_2", array_2);

var array_3 = [];

for (var i in array_2) {
    var subArr = [];
    subArr = getMargeArr(array_2[i], "id");
    Array.prototype.push.apply(array_3, subArr);
}
console.log(array_3);


function getMargeArr(arr, key){
    var newArr = [];
    for (var i in arr) {
        if(typeof arr[i][key] !== undefined){
            var target_val = arr[i][key]; 
            var searchArr = searchArray(newArr, target_val, key)

            if (searchArr === undefined) {
                searchArr = []
                newArr.push(searchArr)
            }
            searchArr.push(arr[i])
        }
    }
    return newArr
}

function searchArray(list, target_val, key) {
    if(list.length !== 0){
        for (var i in list) {
            if (list[i][0][key] === target_val) {
                return list[i]
            }
        }
    }
    return undefined
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+1

 問題点

var array_3 = [
  [
    { id: 0, name: "a"}
  ],
  [
    { id: 2, name: "b"}
  ],
  [
    { id: 3, name: "b"},
    { id: 3, name: "b"}
  ]
]

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

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

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

 new Map (定義順)

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

'use strict';
function toMap (array) {
  return array.reduce(function (map, current) {
    var id = current.id;

    delete current.id;
    map.has(id) ? map.get(id).push(current) : map.set(id, [current]);

    return map;
  }, new Map);
}

var array_1 = [{id: 0, name: "a"}, {id: 2, name: "b"}, { id: 3, name: "b"}, {id: 3, name: "b"}],
    array_2 = [{id: 3, name: "b"}, {id: 3, name: "b"}, {id: 2, name: "b"}, {id: 0, name: "b"}];


console.log(JSON.stringify([...toMap(array_1)]));  // [[0,[{"name":"a"}]],[2,[{"name":"b"}]],[3,[{"name":"b"},{"name":"b"}]]]
console.log(JSON.stringify([...toMap(array_2)]));  // [[3,[{"name":"b"},{"name":"b"}]],[2,[{"name":"b"}]],[0,[{"name":"b"}]]]

 配列 (若番順)

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

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

'use strict';
function toArray (array) {
  return array.reduce(function (array, current) {
    var id = current.id;

    delete current.id;
    id in array ? array[id].push(current) : array[id] = [current];

    return array;
  }, []);
}

var array_1 = [{id: 0, name: "a"}, {id: 2, name: "b"}, { id: 3, name: "b"}, {id: 3, name: "b"}],
    array_2 = [{id: 3, name: "b"}, {id: 3, name: "b"}, {id: 2, name: "b"}, {id: 0, name: "b"}];

console.log(toArray(array_1));  // [[{"name":"a"}],,[{"name":"b"}],[{"name":"b"},{"name":"b"}]]
console.log(toArray(array_2));  // [[{"name":"a"}],,[{"name":"b"}],[{"name":"b"},{"name":"b"}]]

 new Map (若番順)

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

function toMap (array) {
  return new Map(array.reduce(function (array, current) {
    const id = current.id;

    delete current.id;

    for (let element of array) {
      if (element[0] === id) {
        element[1].push(current);
        return array;
      }
    }

    array.push([id, [current]]);
    return array;
  }, []).sort(function (a, b) {
    return a > b;
  }));
}

var array_1 = [{id: 0, name: "a"}, {id: 2, name: "b"}, { id: 3, name: "b"}, {id: 3, name: "b"}],
    array_2 = [{id: 3, name: "b"}, {id: 3, name: "b"}, {id: 2, name: "b"}, {id: 0, name: "b"}];

console.log(JSON.stringify([...toMap(array_1)]));  // [[0,[{"name":"a"}],[2,[{"name":"b"}]],[3,[{"name":"b"},{"name":"b"}]]]
console.log(JSON.stringify([...toMap(array_2)]));  // [[0,[{"name":"a"}]],[2,[{"name":"b"}]],[3,[{"name":"b"},{"name":"b"}]]]

Re: nabanaba さん

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/07/17 11:55 編集

    回答ありがとうございます。
    問題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"}
    ]
    ]

    ```

    キャンセル

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

  • ただいまの回答率 90.53%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • JavaScript

    16320questions

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