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

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

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

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

Q&A

解決済

2回答

1201閲覧

連想配列をキーでグループ化する

退会済みユーザー

退会済みユーザー

総合スコア0

JavaScript

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

0グッド

0クリップ

投稿2020/09/03 22:08

編集2020/09/03 23:33

連想配列をキーでグループ化する方法について
あらかじめ配列のグループキー(category)で順序をソート済みの場合に、
順序を保つ配列にしたいです。

連想配列をキーでグループ化する記事を読んだのですが、
https://teratail.com/questions/85909

この方法だとグループ名(categoryの値)の昇順になってしまうため、下記の取得したいデータ形式に変更したいです。

new map()で連想配列の順序を保つ方法もありますが、それは使わずに、

下記の取得したいデータ形式にする方法がないかと思い考えています。

ご教授お願いします。

javascript

1 var foods = [ 2 { 'name': 'Apple', 'category': 'fruits' }, 3 { 'name': 'Strawberry', 'category': 'fruits' }, 4 { 'name': 'Tomato', 'category': 'vegetables' }, 5 { 'name': 'Carot', 'category': 'vegetables' }, 6 { 'name': 'water', 'category': 'drink' }, 7 { 'name': 'beer', 'category': 'drink' }, 8 ]; 9 10 var expected = {}; 11 $.map(foods, function (item, index) { 12 var category = item["category"]; 13 if (typeof expected[category] == "undefined") { 14 expected[category] = []; 15 } 16 expected[category].push(item); 17 }); 18 19 console.log(expected);

text

1// 取得したいデータ 2  [0] =>[fruit, 3 [ [0] => { 'name': 'Apple'}, [1] => { 'name': 'Strawberry'}, ... ] 4 ], 5  [1] => [vegetables, 6 [ [0] => { 'name': 'Tomato'}, ... ] 7 ], 8  [2] => [drink, 9 [ [0] => { 'name': 'water'}, ... ] 10 ],

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

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

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

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

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

maisumakun

2020/09/03 22:27

「取得したいデータ」のデータ構造が、JavaScriptとして正しくないように見受けられます。「そのようなテキスト」を生成するということでしょうか?
退会済みユーザー

退会済みユーザー

2020/09/03 23:31

コメントありがとうございます。 取得したいデータ形式を修正しました。若干間違っているかもしれませんが、あとでもう一度見直しします。
guest

回答2

0

javascript

1const result=[...new Set(foods.map(x=>x.category))].map(x=>([x,[]])); 2foods.forEach(x=>result.filter(y=>y[0]==x.category)[0][1].push({name:x.name})); 3console.log(result);

投稿2020/09/04 00:40

yambejp

総合スコア116724

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

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

退会済みユーザー

退会済みユーザー

2020/09/04 05:06

コメントありがとうございます。 私にはこの書き方が難しすぎて理解できなく。またアロー演算子が使えない環境のため、 簡単な方法をご教授お願いできますでしょうか?
yambejp

2020/09/04 05:19

ベタになおすとこんなかんじ var result=foods.map(function(x){ return x.category; }).filter(function(x,y,z){ return z.indexOf(x)===y; }).map(function(x){ return [x,[]]; }); foods.forEach(function(x){ result.filter(function(y){ return y[0]==x.category; })[0][1].push({name:x.name}); });
退会済みユーザー

退会済みユーザー

2020/09/04 11:47

教えて頂いた方法で動作を確認することができました。ありがとうございます。
guest

0

ベストアンサー

こんにちは

あらかじめ配列のグループキー(category)で順序をソート済みの場合に、
順序を保つ配列にしたいです。

ということだと、一例として、以下のようにすればよいかと思います。

javascript

1var expected = foods.reduce((a, { name, category }, i, self) => { 2 if (i === 0 || category !== self[i-1].category) { 3 a.push([category, [{ name }]]); 4 } else { 5 a[a.length-1][1].push({ name }); 6 } 7 return a; 8}, []);

投稿2020/09/03 22:41

jun68ykt

総合スコア9058

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

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

退会済みユーザー

退会済みユーザー

2020/09/03 23:32

こんにちは、 コメント、ソースコードをありがとうございます。 おそらく期待している動作だと思います。 一旦、確認してから、ご返信させていただきます。
jun68ykt

2020/09/03 23:39

コメント、ありがとうございます。上記のコードだと、期待値が得られないケースがあれば、コメント頂くか、ご質問に追記いただければと思います。
退会済みユーザー

退会済みユーザー

2020/09/04 04:59

すみません、アロー演算子が使えない環境のため、置き換え方法を知らべていますがわからなく、 ご教授お願いできますでしょうか?
退会済みユーザー

退会済みユーザー

2020/09/04 12:05 編集

コードありがとうございます。 codepenで教えて頂いた方法でうまく実行することができました。 reduceって使い方難しいですね。引数の名前を変更して、どういう動きをしているのか確認してみました。 jQueryで言うとmapとeachが組み合わさったようなイメージかなと感じました。 グループ名がない時は配列を作成し、以降は子要素にデータを追加しているみたいですね。 selfは元データが入っていました。 このif文の条件分岐は高度すぎて自分では思いつかないようなものです。 ありがとうございます。 実際にやりたいことに適応してみました。 Select2のオートコンプリートコンボボックスのグループヘッダ付きを作成するのに これを使用しました。元データにidを追加。 配列の中をオブジェクトに変更しました。 -------------------------------------------------- var data = [  { id: 1, name: 'Apple', category: 'fruits' },  { id: 2, name: 'Strawberry', category: 'fruits' },  { id: 3, name: 'Tomato', category: 'vegetables' },  { id: 4, name: 'Carot', category: 'vegetables' },  { id: 5, name: 'water', category: 'drink' },  { id: 6, name: 'beer', category: 'drink' }, ]; var groupPropertyName = "category"; var idPropertyName = "id"; var displayPropertyName = "name"; var expected = data.reduce(function (result, item, index, self) {  if (index === 0 || item[groupPropertyName] !== self[index - 1][groupPropertyName]) {   result.push(    { text: item[groupPropertyName], children: [{ id: item[idPropertyName], text: item[displayPropertyName] }] }    );  } else {   result[result.length - 1]["children"].push(    { id: item[idPropertyName], text: item[displayPropertyName] }   );  }  return result; }, []); console.log(expected); -------------------------------------------------- これでやりたいことを実施することができました。ありがとうございます。
jun68ykt

2020/09/04 15:06

どういたしまして。 > これでやりたいことを実施することができました。 とのことで、よかったです。 reduce を使いこなせると、様々な場面で重宝します。ちなみに、先ほどのコメントで挙げたコード https://codepen.io/jun68ykt/pen/yLOPzVq?editors=0012 は、reduce を使わないで for ループを使うと以下のような感じになります。 https://codepen.io/jun68ykt/pen/MWyOBZy?editors=0012 ソースコードの読みやすさという観点からは、reduce を無理に使わないで、for ループで書いたほうが、他の人の目に優しいコードになることも、ままありますが、やり過ぎに注意しつつ、forループを使えば書けるコードを、map や filter や reduce を使って書いてみるという練習を日頃からやっていると、だんだんとこれらを使ったコードが自然に(=forループよりも先に)頭に浮かぶようになります。
退会済みユーザー

退会済みユーザー

2020/09/05 13:23 編集

for文の説明までありがとうございます。 とてもわかりやすいですね。 for文のほうとreduceのほうを比較して練習してみようと思います。 i === 0 || e.category !== self[i-1].category が少し理解できたかもしれません。 iが0 または 1つ前のcategoryと等しくなければ、  配列にpush 以外は  配列の子要素の配列にpush 普段、等しいのに == を使うことがほとんどなのですが、厳密な比較 === をなるべく使用したほうがよいのでしょうか? javascriptは 変数の数字が文字列になったりしてしまうこともあるのでデータ型の取り扱いが難しいですね。 reduceの第2引数は aを [] で配列に初期化しているんですね。 なるべく変数を最小限にしようとすると、reduceで 初期化までできるので それも便利だなと思います。 使いすぎはソースがわかりにくくなるので、注意ですね。
jun68ykt

2020/09/06 16:08 編集

こんにちは > iが0 または 1つ前のcategoryと等しくなければ、  配列にpush 以外は  配列の子要素の配列にpush 【回答】はい。上記のような捉え方で合ってます。 > 普段、等しいのに == を使うことがほとんどなのですが、厳密な比較 === をなるべく使用したほうがよいのでしょうか? 【回答】どちらに慣れたほうがいいのか、でいえば、 === のほうかなと思います。 今後どこかで、yuchan01さんがJavaScript で何らかの開発を行うときに、ESLintというコードチェッカーを使う場面があるかもしれません。これは、文法的には間違ってないけれども、問題になりそうなコードを警告してくれるものですが、これが従うルールのひとつに eqeqeq https://eslint.org/docs/rules/eqeqeq というのがあります。これは、「 == の替わりに === を、 != の替わりに !== を使いましょう」というルールです。eqeqeq のデフォルト値は “always” で、どんな状況で == や != を使っていても、 error Expected '===' and instead saw '==' だったり、 error Expected '!==' and instead saw '!=' というエラーメッセージがその箇所に表示されます。ちなみに私は WebStorm という開発ツールを使っていますが、ESLint を適切に設定しておくと、ソースコードで、== や != を書くと即座にその部分に赤い下線が引かれ、マウスを乗せると上記のメッセージが表示されます。(VSCode でも同じようなことができると思います。)自分の場合、JavaScriptを使う業務ではほぼESLint を効かせて、eqeqeq はデフォルトのままなので、 == や != を使わないほうに慣れてしまっています。 === ではなく == でなければダメというのは、簡単にいうと 1 == "1" が true になることを意図的に使っているコードということになりますが、そういうコード自体ちょっとトリッキーなので、右辺の文字列を明示的にnumber にする、 1 === parseInt("1") だったり、単項加算を使って 1 === +"1" というコードに修正します。
jun68ykt

2020/09/06 15:35 編集

おせっかいついでに、もうひとつ。 > 実際にやりたいことに適応してみました。 ということで挙げられているコードを拝読して、idPropertyName や displayPropertyName という変数があったことから、「こういうこともできます」というサンプルを挙げておきます。 https://codepen.io/jun68ykt/pen/RwaxZPe?editors=0012 ・上記のサンプルでは、元のデータ配列 data の要素に、 key と label が追加されています。 ・key はランダムな文字列で id の代わりになるものです。 ・label は name の代わりに使う想定で、name を日本語にした文字列です。 ・これまでどおり、idPropertyName に "id" を、displayPropertyName に"name" を指定した結果をexpected1に得ています。 ・さらに、idPropertyName に "key" を、displayPropertyName に"label" を指定して、意図した expected2 を得ています。 以下、このサンプルの技術的な説明です。MDN のreduce の説明には > reduce() メソッドは、配列の各要素に対して (引数で与えられた) reducer 関数を実行して、単一の結果値にします。 とあります。この説明では、reduce の第一引数に渡す関数を "reducer関数" と言っていますが、このreducer関数を取り替えれば、同じ配列をreduceするのであっても得られる配列が変わります。 先のサンプルは、 reducer関数を作って返す関数 getReducer を作り、これが返す値 を変数 reducer1 と reducer2 に入れ、これらを data.reduce() に渡した結果を、それぞれ、expected1 と expected2 に得ています。 関数getReducer は、引数にgroupPropertyName, idPropertyName, displayPropertyNameを取り、これらの指定されたプロパティ名に従って、期待される動作をする reducer関数を返します。 サンプルでは、reducer2 を得るときに、idPropertyName に”key” を displayPropertyNameに “label” を指定しています。その結果、expected2 では { key: “yR54”, text: “りんご” } というオブジェクトが children の要素となっています。 このように、data.reducer() に渡す関数自体を、別の関数の返り値として得て、変数 reducer1 だったりreducer2 に入れて、reduce の引数として渡せるのは、JavaScript の関数が 「第一級オブジェクト(first-class object)」であるからです。ですので、for ループで書かないで、reduce (だったり map や filter ) を使ったコードを書くという方針は、(それによって読みやすくなるか、というのは別問題として、)JavaScript の関数が第一級オブジェクトであることを駆使したコードを書くこと、と言えます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問