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

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

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

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

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

Q&A

解決済

5回答

345閲覧

スマートな配列処理をしたい

os1_nmnm

総合スコア12

JavaScript

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

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

0グッド

2クリップ

投稿2018/12/28 01:20

編集2018/12/28 02:50

実現したいこと

お店idを格納した配列と、お店の情報が格納された配列があります。

JavaScript

1const shopIds = [1, 2]; 2const shops = [ 3 { id: 1, name: "テラ貴族", isOpen: true}, 4 { id: 2, name: "テラサカバ", isOpen: true}, 5 { id: 3, name: "テラ民", isOpen: true}, 6 { id: 4, name: "テラ木屋", isOpen: true}, 7];

配列shopIdsに格納されたidを持つshopのisOpenフラグをfalseに書き換えるプログラムを書きました。

shopIds.forEach(id => { shops.forEach(shop => { if (shop.id === id) { shop.isOpen = false; } }); });

現在、2重でループさせることでフラグを書き換えていますが、これをmapやfilterなどのメソッドを使ってスマートに書く方法を模索しています。ご教授いただけますと幸いです。

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

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

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

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

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

m.ts10806

2018/12/28 01:24

falseではなくtrueになってますが・・
os1_nmnm

2018/12/28 02:50

ご指摘ありがとうございます。修正いたしました。
miyabi-sun

2018/12/28 02:55

> 配列shopIdsに格納されたidを持つshopのisOpenフラグをfalseに書き換えるプログラムを書きました。 ふんふん…… > shop.isOpen = true; 書けてないやん
guest

回答5

0

ベストアンサー

constで作ったオブジェクトに対して破壊的操作で値をいじるのはあまり良くありません。
という訳で両方のケースで書きます。

今回の質問文には書かれてませんが、
多分このshopIdsは何らかの事情によりWebサイト上で一時的に準備中にしたいという目的で定義された変数だろうと推測されます。
なのでそれ想定で書いてます。


プロパティの値に代入はオブジェクトの破壊的な操作になります。
今回のような破壊的操作を伴う場合、戻り値は不要なのでmapは使わずforEachを使うようにしてください。
mapを使うと未来の貴方や、他のメンバーは「戻り値を使って何かしたいんだ」と勘違いする要因になるので決して使わないでください。

また、こういう2つの配列を使う場合はどちらを起点にするかでコードの長さが変わります。
両方書いてみてしっくり来る方を選びましょう。

shopIdsを先に使うとこんな感じのコードになります。

JavaScript

1shopIds.forEach(id => 2 (shops.find(it => it.id === id) || {}).isOpen = false 3); 4console.log(shops); 5// (4) [{…}, {…}, {…}, {…}] 6// 0: {id: 1, name: "テラ貴族", isOpen: false} 7// 1: {id: 2, name: "テラサカバ", isOpen: false} 8// 2: {id: 3, name: "テラ民", isOpen: true} 9// 3: {id: 4, name: "テラ木屋", isOpen: true}

findの結果で見つからない場合undefinedになり、undefinedのプロパティを操作しようとするとエラーが出ます。
or演算子で空オブジェクトを生成して返すようにすることでプロパティの操作が出来るようにしました。
showIdsが常に存在するIDならば決め打ちでいけるのでもう少しかわいいコードになります

JavaScript

1shopIds.forEach(id => 2 shops.find(it => it.id === id).isOpen = false 3); 4 5// あるか無いか分からない場面だと先にfilter使うんだけど、ちょっと冗長かなぁ? 6shopIds 7 .filter(id => shops.some(it => it.id === id)) 8 .forEach(id => shops.find(it => it.id === id).isOpen = false)

逆にshopsを使えばこのようなコードになるでしょう。

JavaScript

1shops.forEach(it => { 2 if (shopIds.includes(it.id)) it.isOpen = false 3}) 4console.log(shops); 5// (4) [{…}, {…}, {…}, {…}] 6// 0: {id: 1, name: "テラ貴族", isOpen: false} 7// 1: {id: 2, name: "テラサカバ", isOpen: false} 8// 2: {id: 3, name: "テラ民", isOpen: true} 9// 3: {id: 4, name: "テラ木屋", isOpen: true} 10 11// こちらもfilter版、かなりシンプルでスマート! 12shops 13 .filter(it => shopIds.includes(it.id)) 14 .forEach(it => it.isOpen = false)

オブジェクトの破壊的操作を避け、コピーする想定でいくならこんな感じになります。
JavaScriptのプロパティ値に代入する方法は破壊的操作なので使えません。
mapとシャロコピーを併用する方式になります。

shopIdsを主軸にすると変更+変更してないショップ一覧にたどり着くのが困難になりますので、
shops.mapを主軸にするのが適切でしょう。

JavaScript

1const initialisedShops = shops.map(it => ({ 2 ...it, isOpen: shopIds.includes(it.id) ? false : it.isOpen 3})); 4console.log(initialisedShops); 5// (4) [{…}, {…}, {…}, {…}] 6// 0: {id: 1, name: "テラ貴族", isOpen: false} 7// 1: {id: 2, name: "テラサカバ", isOpen: false} 8// 2: {id: 3, name: "テラ民", isOpen: true} 9// 3: {id: 4, name: "テラ木屋", isOpen: true}

投稿2018/12/28 02:38

編集2018/12/28 04:37
miyabi-sun

総合スコア21158

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

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

0

こうでしょうか。

js

1shops.map(shop => shopIds.includes(shop.id) ? {...shop, isOpen: false} : shop);

includes, Object.assign についてはご自身で確認してください。

投稿2018/12/28 01:46

編集2018/12/28 07:03
mather

総合スコア6753

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

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

0

jsは参照渡しているのでmapで返す必要はないですね

javascript

1const shopIds = [1, 2]; 2const shops = [ 3 { id: 1, name: "テラ貴族", isOpen: true}, 4 { id: 2, name: "テラサカバ", isOpen: true}, 5 { id: 3, name: "テラ民", isOpen: true}, 6 { id: 4, name: "テラ木屋", isOpen: true}, 7]; 8shops.forEach(function(x){ 9 x.isOpen=!shopIds.some(function(y){return y=== x.id;}); 10}); 11console.log(shops);

もう少し複雑な条件になった場合も想定するとfilterしてからforEachするという考え方もあります

javascript

1shops.filter(function(x){ 2 return shopIds.some(function(y){return y=== x.id;}); 3}).forEach(function(x){ 4 x.isOpen=false; 5});

訂正版

含まれない場合はそのままという処理としました

javascript

1const shopIds = [1,2]; 2const shops = [ 3 { id: 1, name: "テラ貴族", isOpen: null}, 4 { id: 2, name: "テラサカバ", isOpen: true}, 5 { id: 3, name: "テラ民", isOpen: null}, 6 { id: 4, name: "テラ木屋", isOpen: true}, 7]; 8shops.forEach(function(x){ 9 if(shopIds.some(function(y){return y=== x.id;})){ 10 x.isOpen=false; 11 } 12}); 13console.log(shops);

投稿2018/12/28 01:45

編集2018/12/28 03:21
yambejp

総合スコア114839

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

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

miyabi-sun

2018/12/28 02:50

前半のコードがroot_jpさんの所にも書いた件と同様で、要件満たしてなさそうです。
yambejp

2018/12/28 03:22

たしかに
miyabi-sun

2018/12/28 04:21

対応ありがとうございます!
yambejp

2018/12/28 04:25

いえとんでもない、ご指摘ありがとうございます
guest

0

JavaScript

1const result = shops.map(shop => { 2 shop.isOpen = !shopIds.includes(shop.id); 3 return shop; 4}); 5console.log(result); 6/* 7 * 0: Object { id: 1, name: "テラ貴族", isOpen: false } 8 * ​1: Object { id: 2, name: "テラサカバ", isOpen: false } 9 * ​2: Object { id: 3, name: "テラ民", isOpen: true } 10 * ​3: Object { id: 4, name: "テラ木屋", isOpen: true } 11 */

shopIdsに含まれていない場合にはisOpenを更新しない版

JavaScript

1var result = shops.map(shop => { 2 if (shopIds.includes(shop.id)) shop.isOpen = false; 3 return shop; 4});

投稿2018/12/28 01:28

編集2018/12/28 04:49
root_jp

総合スコア4666

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

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

miyabi-sun

2018/12/28 03:00 編集

> 配列shopIdsに格納されたidを持つshopのisOpenフラグをfalseに書き換えるプログラムを書きました。 これだとidを持たない場合はisOpenがtrueに書き換わっているので要件を満たしていませんね。 質問文自体の日本語がダメで実はこれが正解の可能性もありますが、コードも違うと言ってるように見えます。 また、オブジェクトのプロパティを触っている為破壊的変更になっておりshopsの中身が書き換わってます。 これでresult変数を宣言してmapを使っていることから、 他のエンジニアは「ああ、ちゃんとシャロコーピー使ってるから大丈夫なんだな」と勘違いする要因になり危険です。 変数宣言+mapの方針で行くのであれば、7Kreuzさんの回答のようにしっかりシャローコピーを掛けた方が良いでしょう。
root_jp

2018/12/28 04:41

> idを持たない場合はisOpenがtrueに書き換わっている これはダメですね。。。 constのオブジェクトのデータを書き換えてるのも良くないです。 今回のオブジェクトは全てプリミティブ型で構成されているので、 確かにシャローコピーすれば十分そうですね。 この辺りの副作用は気を付けなければならないなぁ。。。
guest

0

こんなんでどうでしょう

javascript

1const shops2 = shops.map(shop => ({ 2 ...shop, isOpen: !shopIds.includes(shop.id) 3}))

投稿2018/12/28 01:36

編集2018/12/28 01:38
7Kreuz

総合スコア112

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

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

miyabi-sun

2018/12/28 02:50

root_jpさんの所にも書いた件と同様で、要件満たしてなさそうです。 コード自体はとても洗練されてて好きです。
7Kreuz

2018/12/28 04:43

あ、確かにそうですね>要件 ご指摘ありがとうございます
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問