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

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

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

GraphQL は、アプリケーション・プログラミング・インタフェース (API) 向けのクエリ言語およびサーバーサイドランタイムです。APIの速度、柔軟性、開発者にとっての使いやすさを向上させるために設計され、データを複数のデータソースから取得するリクエストを1つのAPI呼び出しで構成できます。

Node.js

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

Q&A

解決済

3回答

2108閲覧

オブジェクトを見やすく変換したい

yuki_90453

総合スコア326

GraphQL

GraphQL は、アプリケーション・プログラミング・インタフェース (API) 向けのクエリ言語およびサーバーサイドランタイムです。APIの速度、柔軟性、開発者にとっての使いやすさを向上させるために設計され、データを複数のデータソースから取得するリクエストを1つのAPI呼び出しで構成できます。

Node.js

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

0グッド

1クリップ

投稿2021/10/28 07:59

編集2021/10/28 08:29

#概要
GraphQLのレスポンスが下記のようになっております。

{ "products": { "edges": [ { "node": { "description": "Sleek and black", "featuredImage": { "id": "gid://shopify/ProductImage/15464783282345", "originalSrc": "https://cdn.shopify.com/s/files/1/0268/1005/6873/products/bb20.jpg?v=1602116082" }, "handle": "skirt", "id": "gid://shopify/Product/4773734482089", "images": { "edges": [ { "node": { "id": "gid://shopify/ProductImage/15464783282345", "originalSrc": "https://cdn.shopify.com/s/files/1/0268/1005/6873/products/bb20.jpg?v=1602116082" } }, { "node": { "id": "gid://shopify/ProductImage/26072838406313", "originalSrc": "https://cdn.shopify.com/s/files/1/0268/1005/6873/products/bb7.jpg?v=1612636091" } }, { "node": { "id": "gid://shopify/ProductImage/26072838373545", "originalSrc": "https://cdn.shopify.com/s/files/1/0268/1005/6873/products/bb6.jpg?v=1612636091" } } ] } } } ] } }

#やりたいこと
edgesの値は配列。nodeの値はオブジェクトとなっています。
これを下記のようにedgesやnodeのない値に変換したいと考えております。

{ "products": [ { "description": "Sleek and black", "featuredImage": { "id": "gid://shopify/ProductImage/15464783282345", "originalSrc": "https://cdn.shopify.com/s/files/1/0268/1005/6873/products/bb20.jpg?v=1602116082" },

#Use Playgreound
Use Playgreoundにアップロードしました。
Use Playgreound

#質問
なんとなく、objectを配列ように扱って各keyの名前がedgesやnodeで処理加えればいいのかなと思います。
しかし、いくつか問題点があります。

  • 多次元オブジェクトであること
  • オブジェクトを配列のように扱って、そのあとオブジェクトに戻すという方法が思いつきません。

どのようにすれば、「やりたいこと」に書いてるコードのように変換できますでしょうか?

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

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

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

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

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

退会済みユーザー

退会済みユーザー

2021/10/28 08:10

要件を確認させてください。主旨としては、「すべての node が並んだ、一次元の配列が欲しい」ということでよいでしょうか?
退会済みユーザー

退会済みユーザー

2021/10/28 09:44

ご返信ありがとうございます。上手い解法が思いつきましたら回答します。
guest

回答3

0

ベストアンサー

多次元オブジェクトであること

再帰関数を使って掘り進める事になります。
フィボナッチ数列にしか使えないテクニックではなく、
意外とこういう再帰的に掘り進めたいケースで使われます。

因みにJavaScriptに於ける再帰関数は無限に掘り進めるわけではなく、
ChromeやFirefox等に積んでいるJavaScriptエンジンに左右されます。
だいたい1000階層くらいになるとエラーで死ぬので、それより浅いと想定されるケースでしか使えません。

今回のケースではそんなアホみたいな巨大なJSONは帰ってこないと思うので
再帰関数を利用するのが最善でしょう。

オブジェクトを配列のように扱って、そのあとオブジェクトに戻すという方法が思いつきません。

Object.fromEntries
Object.entries
これで双方向に変換出来ます。


それではやっていきましょう。
とりま質問文の値をres変数にでも保管しましょうか。

js

1const res = { 2 "products": { 3 "edges": [ 4 { 5 "node": { 6 "description": "Sleek and black", 7 "featuredImage": { 8 "id": "gid://shopify/ProductImage/15464783282345", 9 "originalSrc": "https://cdn.shopify.com/s/files/1/0268/1005/6873/products/bb20.jpg?v=1602116082" 10 }, 11 "handle": "skirt", 12 "id": "gid://shopify/Product/4773734482089", 13 "images": { 14 "edges": [ 15 { 16 "node": { 17 "id": "gid://shopify/ProductImage/15464783282345", 18 "originalSrc": "https://cdn.shopify.com/s/files/1/0268/1005/6873/products/bb20.jpg?v=1602116082" 19 } 20 }, 21 { 22 "node": { 23 "id": "gid://shopify/ProductImage/26072838406313", 24 "originalSrc": "https://cdn.shopify.com/s/files/1/0268/1005/6873/products/bb7.jpg?v=1612636091" 25 } 26 }, 27 { 28 "node": { 29 "id": "gid://shopify/ProductImage/26072838373545", 30 "originalSrc": "https://cdn.shopify.com/s/files/1/0268/1005/6873/products/bb6.jpg?v=1612636091" 31 } 32 } 33 ] 34 } 35 } 36 } 37 ] 38 } 39}

ドキュメントによると
エッジはノードとカーソルを持ち、カーソルはページネーション用なので
画面表示には使わないので捨てるという感じでしょうかね。

つまりedgeキーを見つけたら配列を展開して
nodeを取り出してそれに置きかえるという事をやりましょうか。

js

1const removeEdge = obj => { 2 if (typeof obj != 'object') return obj; 3 return Object.fromEntries( 4 Object.entries(obj).map(([key, val]) => { 5 if (!Array.isArray(val.edges)) return [key, val]; 6 return [ 7 key, 8 // edgesが配列であるのでmapで変換して 9 // 再度removeEdgeで精査している 10 val.edges.map(it => removeEdge(it?.node)) 11 ]; 12 }) 13 ); 14};

実際コードにしたら結構色々テクニックを使ってますね。

JavaScriptはnullやundefined値にプロパティを所持出来ません。
これらの値にアクセスを試みただけでJavaScriptがエラーで落ちるので、
edge[n].nodeを決め打ちで参照する時に利用しています。

js

1JSON.stringify(removeEdge(res), null, 2);

JSON

1{ 2 "products": [ 3 { 4 "description": "Sleek and black", 5 "featuredImage": { 6 "id": "gid://shopify/ProductImage/15464783282345", 7 "originalSrc": "https://cdn.shopify.com/s/files/1/0268/1005/6873/products/bb20.jpg?v=1602116082" 8 }, 9 "handle": "skirt", 10 "id": "gid://shopify/Product/4773734482089", 11 "images": [ 12 { 13 "id": "gid://shopify/ProductImage/15464783282345", 14 "originalSrc": "https://cdn.shopify.com/s/files/1/0268/1005/6873/products/bb20.jpg?v=1602116082" 15 }, 16 { 17 "id": "gid://shopify/ProductImage/26072838406313", 18 "originalSrc": "https://cdn.shopify.com/s/files/1/0268/1005/6873/products/bb7.jpg?v=1612636091" 19 }, 20 { 21 "id": "gid://shopify/ProductImage/26072838373545", 22 "originalSrc": "https://cdn.shopify.com/s/files/1/0268/1005/6873/products/bb6.jpg?v=1612636091" 23 } 24 ] 25 } 26 ] 27}

ただまぁ、エラー処理周りとか適当ですし、
そもそも要件を満たしているかは知らないので責任は持てません。

質問文を読んだ上で、「こういうアプローチで何とかなりそう」程度の指針を示す事は出来たかなと思います。

ここから更に改良して相応しいエラー処理等を付け足してみてください。

投稿2021/10/28 09:32

miyabi-sun

総合スコア21203

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

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

0

追記

変換後のデータ形式の認識に齟齬があったようなので、修正しました。


こういったデータ変換系の話は、一歩々々に分けて考えるとやりやすいです。

というわけで、まずは{ "node": { "description": ..., ... } }{ "description": ..., ... }に変換するコードを考えます。

js

1const edge = { "node": { "description": ..., ... } }; 2 3const node = edge["node"];

次は{ "edges": [...] }[...]に変換するコードです。

js

1const products = { "edges": [...] }; 2 3const edges = products["edges"];

上記のことから、{ "products": { "edges": [{ "node": { "description": ... } }, ...] } }{ "products": [{ "description": ... }, ...]に変換するのは以下のように書けます。

js

1const data = { 2 "products": { 3 "edges": [{ 4 "node": { 5 "description": "Sleek and black", 6 "featuredImage": { 7 "id": "gid://shopify/ProductImage/15464783282345", 8 "originalSrc": "https://cdn.shopify.com/s/files/1/0268/1005/6873/products/bb20.jpg?v=1602116082" 9 }, 10 "handle": "skirt", 11 // ... 12 }, 13 }], 14 }, 15}; 16 17const result = { 18 "products": data["products"]["edges"].map(edge => edge["node"]) 19};

"images"の中身も入れ子にはなっていますが、同様に処理できます。
一点、{ ...node, "image": 〜 }としてnode["image"]だけを書き換えていることに注意です。
で、これらを踏まえ、こんな感じでどうでしょうか。

js

1const data = { 2 "products": { 3 "edges": [{ 4 "node": { 5 "description": "Sleek and black", 6 "featuredImage": { 7 "id": "gid://shopify/ProductImage/15464783282345", 8 "originalSrc": "https://cdn.shopify.com/s/files/1/0268/1005/6873/products/bb20.jpg?v=1602116082" 9 }, 10 "handle": "skirt", 11 "images": { 12 "edges": [{ 13 "node": { 14 "id": "gid://shopify/ProductImage/15464783282345", 15 "originalSrc": "https://cdn.shopify.com/s/files/1/0268/1005/6873/products/bb20.jpg?v=1602116082" 16 }, 17 }], 18 }, 19 // ... 20 }, 21 }], 22 }, 23}; 24 25const result = { 26 "products": data["products"]["edges"].map( 27 edge => edge["node"] 28 ).map( // ここまでで{ "products": [{ "description": ... }, ...] }になっている 29 node => ({ 30 ...node, 31 "images": node["images"]["edges"].map( 32 edge => edge["node"] 33 ) 34 }) 35 ), 36} 37 38console.log(JSON.stringify(result, null, 2)); 39 40/* result 41{ 42 "products": [{ 43 "description": "Sleek and black", 44 "featuredImage": { 45 "id": "gid://shopify/ProductImage/15464783282345", 46 "originalSrc": "https://cdn.shopify.com/s/files/1/0268/1005/6873/products/bb20.jpg?v=1602116082", 47 }, 48 "handle": "skirt", 49 "images": [{ 50 "id": "gid://shopify/ProductImage/15464783282345", 51 "originalSrc": "https://cdn.shopify.com/s/files/1/0268/1005/6873/products/bb20.jpg?v=1602116082", 52 }], 53 }], 54} 55 */

投稿2021/10/28 09:12

編集2021/10/28 10:18
fj68

総合スコア752

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

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

yuki_90453

2021/10/28 09:31

ご回答ありがとうございます! さらに下層の、products.edges[0].node.imagesの部分にもまだedgesが残っております。 下層もすべて変換させる方法はないでしょうか?
fj68

2021/10/28 09:58

変換後のデータ形式の認識に齟齬があったようなので、回答を修正しました。 ご確認ください。
yuki_90453

2021/10/29 06:43

ご回答ありがとうございます!勉強になりました。
guest

0

一案を回答します。

javascript

1function convert(obj, edgesHolderProp = 'products', depth = 0) { 2 if (depth === 50) { 3 return { [edgesHolderProp]: [] }; 4 } 5 6 const nodes = obj[edgesHolderProp].edges.map(e => { 7 const node = _.omit(e.node, 'images'); 8 if (e.node.images) { 9 const { images } = convert(e.node, 'images', depth + 1); 10 node.images = images; 11 } 12 return node; 13 }); 14 15 return { [edgesHolderProp]: nodes }; 16}

という再帰関数を作っておきます。この関数convert()では、

  • ブジェクトの複製を作る際に、コピーしたくない不要なプロパティを指定できる、lodash の _.omit を使っています。

  • 再帰の深さの上限を 仮に50にしています。

これを使って、質問にある original から edgesnode を削除したオブジェクト converted を得るには

javascript

1const converted = convert(original);

とします。

使用例を挙げておきます。ご質問にあるUse Playgreoundにアップロードされているconst original に含まれる以下のオブジェクト

json

1"node": { 2 "id": "gid://shopify/ProductImage/26072838406313", 3 "originalSrc": "https://cdn.shopify.com/s/files/1/0268/1005/6873/products/bb7.jpg?v=1612636091" 4}

に、もう一階層 images を追加した JSON を以下に作成しました。

???? https://demo1825326.mockable.io/tera/questions/366645.json

これをテストデータとして、冒頭の関数 convert() を試したサンプルが以下です。

???? codepen.io/kilesa/pen/GRvEwPR

GraphQLの仕様の詳細は確認しておりませんので、要件と異なる点などあれば、コメントからご教示ください。

追記

上記の回答では、lodash の _.omit を使いましたが、同じくlodashの _.mapValues を使えば、convert()関数を以下のように書けました。(再帰の深さの上限は、先と同じく50としています。)

javascript

1const convert = (obj, depth = 0) => 2 depth === 50 3 ? {} 4 : _.mapValues(obj, value => 5 value.edges 6 ? value.edges.map(({ node }) => convert(node, depth + 1)) 7 : value, 8 );

???? codepen.io/kilesa/pen/MWvoMgy

投稿2021/10/28 15:25

編集2021/10/28 18:32
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

yuki_90453

2021/10/29 06:44

回答ありがとうございます!勉強になりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問