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

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

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

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

JavaScript

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

Q&A

解決済

3回答

680閲覧

2つの配列からやや複雑な条件でデータを抽出したい

退会済みユーザー

退会済みユーザー

総合スコア0

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

JavaScript

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

0グッド

0クリップ

投稿2017/09/28 05:18

編集2017/09/29 04:10

以下のようなリストから、その下に掲載している配列を抽出する為には、
どのようにするのが良いのでしょうか?

やりたいこととしては、orderList配列の中の各オブジェクトの中にある、
products配列の中にあるプロダクトIDとitemList配列の中の各オブジェクトの中のidの値が同一な場合に、
そのitemList配列内の同一プロダクトidを持つオブジェクト内のitems配列の中にある各オブジェクトを取り出し、
それらを1つの配列の中に1次元配列としてまとめたいです。

文章にすると、ややこしいですが、やりたいことはシンプルなのです。

一応、自分で得たい結果は取得するコードは既に書いたのですが、

  1. mapファンクションでまずorderListの中から各products配列を抽出
  2. mapファンクションで抽出した配列は2次元になってしまってるので、それをreduceファンクションを使って、array.concat()で1次元配列化したものを抽出

3. 2で取得したプロダクトIDリストから重複するプロダクトIDを削除(フィルターファンクションを使いました)
4. 3で取得したプロダクトIDリストとマッチするitemList内のitem配列をmapファンクションで抽出
5. ....
6. ....

と6ステップかけてようやく得たい結果を超手続き型な感じで取り出すことができました。。。

なんか違う感があるのですが、皆様ならどのように書くのでしょうか?

※補足: なんか違う感というのは、mapなどのメソッド使うと直ぐに多次元配列になってしまって、それをまた、他の処理の為に1次元配列に戻さないといけなかったり、更にそういうのが二回必要だったりと、もっと短く出来る方法ってないのかなと思いまして。冗長に感じるということです。

入力

const itemList = [ { id: 'p1', items: [ { id: 1 }, { id: 2 }, { id: 3 } ] }, { id: 'p2', items: [ { id: 1 }, { id: 10 }, { id: 33 } ] }, { id: 'p3', items: [ { id: 11 }, { id: 22 }, { id: 33 } ] } ]; const orderList = [ { id: '1', products: [ 'p1' ] }, { id: '2', products: [ 'p3' ] } ];

得たい結果

const items = [ { id: 1 }, { id: 2 }, { id: 3 }, { id: 11 }, { id: 22 }, { id: 33 } ]

補足

lodash使えば一発だよ!みたいな回答でもありがたいです。

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

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

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

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

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

kei344

2017/09/28 05:30

ご自身で書かれたコードを質問文に追記されたほうが回答を得られやすいと思います。
Lhankor_Mhy

2017/09/28 05:33

結果に { id: 11 } などが含まれる理由が分からないです。『同一プロダクトidを持つオブジェクト』の当方の解釈が間違っているのかもしれません。『同一プロダクトidを持つオブジェクト』は、id: 'p1' と id: 'p3' ではないのですか?
yambejp

2017/09/28 05:34

p1,p3を抽出してそれに合致するitemのidを取るとして,なぜp2にしかないid11,12,13が対象になっているのでしょうか?
退会済みユーザー

退会済みユーザー

2017/09/28 05:37

ご指摘ありがとうございます!大変失礼致しました。修正致しました。
yambejp

2017/09/28 05:48

orderList内の productsは配列を返しますが、これは要素数は必ず1個なのでしょうか?それとも0だったり、2個以上だったり、また複数の場合は重複があったりするのでしょうか?
退会済みユーザー

退会済みユーザー

2017/09/28 05:53

要素数は複数存在し得ますが、各products内の要素は重複しません。ただし、orderList全体でみると、重複するプロダクトidは存在し得ます。
think49

2017/09/29 02:02 編集

ロジックは分かりましたが、現在のコードが書かれていないので「やりたいことだけを書いた質問」になっていると思います。「なんか違う感」とありますが、違う感を具体化した方が良いと思います。効率面での無駄を気にしているのか、コードが冗長だと思っているのか、など。いずれにしても現在のコードは開示した方が良いと思います。また、ご掲示のコードで要件を満たすだけなら、オブジェクトの構造上無駄が多いのですが、構造は変更できないのでしょうか。
退会済みユーザー

退会済みユーザー

2017/09/29 02:15 編集

apiから取れるデータがあのような構造なので、自分の方ではコントロール出来ないのです。。 なんか違う感というのは、mapなどのメソッド使うと直ぐに多次元配列になってしまって、それをまた、他の処理の為に1次元配列に戻さないといけなかったり、更にそういうのが二回必要だったりと、もっと短く出来る方法ってないのかなと思いまして。冗長に感じるということです。
think49

2017/09/29 02:16

把握しました。現在のコードの開示、違う感の具体化についての回答をお願いします。
退会済みユーザー

退会済みユーザー

2017/09/29 02:20

違う感追加しました
think49

2017/09/29 02:23

「入力」が変わっているだけで「違う感」についての説明がないようですが、どこを指しているのでしょうか。また、「入力」を変更したら「得たい結果」も変わらなければなりません。 https://teratail.com/questions/history-questions/94225
think49

2017/09/29 02:24

現在のコードについても追記をお願いします。求めているのは「得たい結果を得るためのコード」です。
退会済みユーザー

退会済みユーザー

2017/09/29 02:39

2017/09/29 11:1のコメントに違う感の補足を編集で行いました。(という意味でした。)
退会済みユーザー

退会済みユーザー

2017/09/29 02:51 編集

【求めているのは「得たい結果を得るためのコード」です。】とありますが、その理由がいまいちわかりません。【現在のコードが書かれていないので「やりたいことだけを書いた質問」になっていると思います。】ということですが、「やりたいことだけを書いた質問」ではなくて「皆さんならどのように書くのか」についての質問です。動かないコードの修正箇所を探しているわけでもなく、自分のコードをベースにリファクタリングをお願いしている訳でもないので、あえて、ざっくりと自分のコードの実装方法のみを掲載しております。
think49

2017/09/29 03:13

質問本文を2017/09/29 11:1で検索しましたが、補足されている場所を発見出来ませんでした。修正した影響で入力と得たい結果の整合性がとれなくなっていることから、この修正はミスではないかと思うのですが…。
退会済みユーザー

退会済みユーザー

2017/09/29 03:19

2017/09/29 11:1の【コメント】と書きました通り、本文ではなくコメントでお答え致しました。
退会済みユーザー

退会済みユーザー

2017/09/29 03:21 編集

入力と得たい結果の整合性はとれております。修正前が整合性が取れておりませんでした。
think49

2017/09/29 03:25

コードを求める理由は、言葉だけのやりとりでは齟齬が発生する可能性があること、コード制作依頼ではないことをはっきりさせるため、です。「皆さんならどのように書くのか」は「コードを書いて下さい」とほぼ同義だと私は思います。コードに限定していないので、方法を書くだけで要件を満たしてはいますが、アルゴリズムを書くことはコードを書くよりも手間がかかりますので、コードだけの回答になりがちです。コードを書いた方が間違いなく質問者に伝わることを経験的に知っている人は尚更にコードを書きます。方法だけを書いた回答をして、それが質問者に伝わるのか、満足するのか、は別問題なので、回答に反応する形で質問を受け付けなければならず、それは回答者の手間となります。
think49

2017/09/29 03:29

コメントの補足の件、理解しました。読み過ごしてしまい、すみませんでした。ただ、質問スレは後で同じ質問に遭遇した人が参考にする共有資産なので、本文にも反映した方がよいと思います。
退会済みユーザー

退会済みユーザー

2017/09/29 04:11

> 本文にも反映した方がよいと思います。反映致しました。
guest

回答3

0

ベストアンサー

いやー、そんな方法しかないと思いますよ。
あえて工夫するとするなら、とりあえずitemListを辞書化するとか?

javascript

1 ( 2 ( itemDict, productsIds ) => 3 [].concat( 4 ...productsIds.map( e => itemDict[e] ) 5 ) 6 )( 7 Object.assign( ...itemList.map( ({id,items}) => ({[id]:items}) ) ), 8 [...new Set( 9 [].concat( 10 ...orderList.map( ({products}) => products ) 11 ) 12 )] 13 );

コメントを受けてIDをユニークに

javascript

1 ( 2 ( {itemDict, productsIds} ) => 3 [...new Set( 4 [].concat( 5 ...productsIds.map( e => itemDict[e] ) 6 ).map( e => JSON.stringify(e) ) 7 )].map( e => JSON.parse(e) ) 8 )({ 9 itemDict: 10 Object.assign( ...itemList.map( ({id,items}) => ({[id]:items}) ) ) 11 , 12 productsIds: 13 [...new Set( 14 [].concat( 15 ...orderList.map( ({products}) => products ) 16 ) 17 )] 18 , 19 });

投稿2017/09/28 07:00

編集2017/09/29 07:55
Lhankor_Mhy

総合スコア36074

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

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

miyabi-sun

2017/09/29 01:23

[...new Set(array)]と[].concat(...array)のイディオム初めて見ました。 めっちゃパワフルで捗りますね。 因みに現回答だとorderListのproductsの中身をp1とp2にするとid1のオブジェクトが2個になってしまうので要調整ですね。 例題だと別オブジェクト({id: 1} == {id: 1} // false)なんで[...new Set()]のイディオムが使えないのが厄介そうです。
退会済みユーザー

退会済みユーザー

2017/09/29 02:45

ご回答ありがとうございます。...はspread operatorですよね?配列の中身が取り出されるやつですね。 ()から始まる書き方に自分は馴染みがないので、ぱっと見て理解が自分には出来ていませんが、研究させていただきます!ありがとうございます。setなんて使ったことない!immutable.jsで使ったことあるくらいだ。
Lhankor_Mhy

2017/09/29 02:48

あー、やっぱりそこユニークにしないとダメですかねー、明示がなかったので手抜きましたが← > 別オブジェクト({id: 1} == {id: 1} // false)なんで[...new Set()]のイディオムが使えないのが厄介そう うええ……
退会済みユーザー

退会済みユーザー

2017/09/29 02:55

美しいです。。。
退会済みユーザー

退会済みユーザー

2017/09/29 03:07

[].concat(...arr)の使い方を学べたのはとても有り難いです!ありがとうございました♪
miyabi-sun

2017/09/29 04:30

確かにユニーク云々は明示されてませんね、行き過ぎた指摘でした! もしユニークにしたければその時だけRamda.jsやLodashのuniqByを解禁すれば十分かもしれませんね。
Lhankor_Mhy

2017/09/29 08:05

> miyabi-sunさん JSONにして処理してから戻す、というメモリを食いそうなやり方で書いてみました! ついでに可読性に配慮して分割代入にしてみましたが、なんか余計に読みにくくなったかもです。 > hayatomoさん BAありがとうございます。 でも、あえてワンライナーを意識して書いたので、読みにくいコードなんです。こういうのは、本来は美しくないと思います。
guest

0

仕様によっては調整が必要かもしれませんが
とりあえずこんな感じです

※items内の同じidのオブジェクトは除外するよう調整しました

javascript

1<script> 2var itemList = [ 3 {id: 'p1',items: [{id: 1},{id: 2},{id: 3},{id: 3}]}, 4 {id: 'p2',items: [{id: 1},{id:10},{id:33}]}, 5 {id: 'p3',items: [{id:11},{id:22},{id:33}]}, 6 ]; 7var orderList = [ 8 {id:'1',products: ['p1']}, 9 {id:'2',products: ['p2']}, 10 {id:'3',products: ['p2']}, 11 {id:'4',products: []}, 12 ]; 13 14var p=[]; 15orderList.map(function(i){ 16 Array.prototype.push.apply(p,i.products); 17}); 18p=p.filter(function(i,j,k){return k.indexOf(i)==j;}); 19/*orderListのユニークなproductId*/ 20console.log(p); 21 22var items=[]; 23itemList.map(function(i){ 24 if(p.indexOf(i.id)>=0){ 25 Array.prototype.push.apply(items,i.items); 26 } 27}); 28 29items=items.filter(function(i,j,k){ 30 return k.map(function(x){return x.id;}).indexOf(i.id)==j; 31}); 32console.log(items);

投稿2017/09/28 06:16

編集2017/09/29 02:30
yambejp

総合スコア114784

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

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

退会済みユーザー

退会済みユーザー

2017/09/29 02:17

ご回答ありがとうございます!参考にさせて頂きます。applyやcallって使いこなせてないので、自分の引き出しに直ぐ使えるものとしてないんですよね。参考にさせて頂きます!
yambejp

2017/09/29 02:32

別解の補足にあるよう、 itemsの中のオブジェクトがidをキーにユニークとのことなので調整しました
退会済みユーザー

退会済みユーザー

2017/09/29 02:53

すみません。明確に情報をお伝え出来ておりませんで。。。
guest

0

真正面からやるとこう、JavaScriptだと表現力が足りないからRamda.js使うことに…
ついでにポイントフリーで挑戦。

JavaScript

1const R = require('ramda'); 2const itemList = [ 3 {id: 'p1', items: [{id: 1}, {id: 2}, {id: 3}]}, 4 {id: 'p2', items: [{id: 1}, {id: 10}, {id: 33}]}, 5 {id: 'p3', items: [{id: 11}, {id: 22}, {id: 33}]} 6]; 7const orderList = [ 8 {id: '1', products: ['p1']}, 9 {id: '2', products: ['p3']} 10]; 11 12const results = R.pipe( 13 R.pluck('products'), 14 R.flatten, 15 R.map(R.pipe(R.propEq('id'), R.find(R.__, itemList))), 16 R.filter(R.identity), 17 R.pluck('items'), 18 R.flatten, 19 R.uniqBy(R.prop('id')) 20)(orderList) 21 22console.log(results) 23[ { id: 1 }, 24 { id: 2 }, 25 { id: 3 }, 26 { id: 11 }, 27 { id: 22 }, 28 { id: 33 } ]

やることが複雑すぎてそんなに短くならない。
すぐに二重配列になるからflattenを何度も挟むことになるしね。

より関数的にするなら経由する関数に名称付けてこんな感じ

JavaScript

1const R = require('ramda'); 2const itemList = [ 3 {id: 'p1', items: [{id: 1}, {id: 2}, {id: 3}]}, 4 {id: 'p2', items: [{id: 1}, {id: 10}, {id: 33}]}, 5 {id: 'p3', items: [{id: 11}, {id: 22}, {id: 33}]} 6]; 7const orderList = [ 8 {id: '1', products: ['p1', 'p2']}, 9 {id: '2', products: ['p3']} 10]; 11 12// この関数群を他のファイルにしてrequireすればテストしやすい 13// toUniqProducts :: [{products: [String]}] -> [String] 14const toUniqProducts = R.pipe(R.pluck('products'), R.flatten, R.uniq) 15// toUniqItems :: [String] -> [Object] 16const toUniqItems = R.pipe( 17 R.map(R.pipe(R.propEq('id'), R.find(R.__, itemList))), 18 R.filter(R.identity), 19 R.pluck('items'), 20 R.flatten, 21 R.uniqBy(R.prop('id')) 22) 23 24R.compose( 25 R.tap(console.log), 26 toUniqItems, 27 toUniqProducts, 28)(orderList)

Ramda.jsって便利だね!
素のJSで頑張るならLhankor_Mhyさんのアプローチがエレガント。

投稿2017/09/28 08:01

編集2017/09/29 01:46
miyabi-sun

総合スコア21158

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

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

退会済みユーザー

退会済みユーザー

2017/09/29 02:46

ramda.jsなんてものがあるのですね!関数型プログラミングをちゃんと勉強したあとに、そのライブラリを触って見たいと思います。Lhankor_Mhyさんのアプローチ、研究します!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問