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

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

新規登録して質問してみよう
ただいま回答率
85.37%
多次元配列

1次元配列内にさらに配列を格納している配列を、多次元配列と呼びます。

JavaScript

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

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

Q&A

解決済

2回答

2785閲覧

ネストされた連想配列の更新について

underfield

総合スコア4

多次元配列

1次元配列内にさらに配列を格納している配列を、多次元配列と呼びます。

JavaScript

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

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

0グッド

1クリップ

投稿2021/11/13 15:09

前提・実現したいこと

[https://mui.com/components/checkboxes/#indeterminate]
上記URLにあるようなコンポーネント作成したいと思い、
親子関係にあるチェックボックス操作で、チェック状態を維持したいと思っています。
子のチェックボックスが変わったら、checkedをtrueにしたいのですが連想配列の操作がうまく行っていません。

JSON

1{ 2 "value": [ 3 { 4 "parent_id": "2020", 5 "state": [ 6 { 7 "child_id": "100", 8 "checked": false 9 }, 10 { 11 "id": "200", 12 "checked": false 13 } 14 ] 15 }, 16 { 17 "parent_id": "2021", 18 "state": [ 19 { 20 "child_id": "3", 21 "checked": false 22 } 23 ] 24 } 25 ] 26}

発生している問題・エラーメッセージ

Cannot assign to read only property 'checked' of object Cannot set properties of undefined

該当のソースコード

checkboxのchangeイベントで下記のように該当するchild_idを特定し、checkedを書き換えようとしています。
一旦、一時的な更新データを作成した上で、スプレッド構文にてデータの更新を試みようとしています。

javascript

1checkArray.forEach((checkData, index) => { 2 const result = checkData.state.findIndex((data) => data.id === e.target.value); 3 if (result !== -1) { 4 // 親データを取り出し、一時領域に格納 5 tmpParent.year = checkData.parent_id; 6 tmpParent.state = []; 7 8 // 子のデータを取り出し更新、一時領域に格納 9 tmpChild = { ...checkData.state[result], ...{ checked: true } }; 10 tmpParent.state[tmpParent.state.length] = tmpChild; 11 12 const tmp = { ...checkData.state, ...tmpParent.state }; 13 tmpParent.state = tmp; 14 15 } 16});

試したこと

連想配列で下記のように処理してもエラーとなってしまい、スプレッド構文で更新をしようとしています。

javascript

1checkArray[index].stete[result].checked = true

補足情報(FW/ツールのバージョンなど)

react 17.2
recoil 0.5.2

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

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

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

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

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

guest

回答2

0

ベストアンサー

[https://mui.com/components/checkboxes/#indeterminate]

上記URLにあるようなコンポーネント作成したいと思い、

そちらのMaterial UIのドキュメントにあるサンプルコード をforkして、質問にあるJSONに含まれる配列を初期stateとして、それっぽく動くものにしました。

???? forkしてそれっぽく動くようにしたもの

(※JSONの配列要素"parent_id":"2022"をひとつ足しています。また、子要素の配列であるstateというプロパティ名は、Reactコンポーネントのstateと混同してしまうため、children に変えています。)

jsx

1import * as React from "react"; 2import Box from "@mui/material/Box"; 3import Checkbox from "@mui/material/Checkbox"; 4import FormControlLabel from "@mui/material/FormControlLabel"; 5 6const DEFAULT_ARRAY = JSON.parse(`{ 7 "value":[ 8 { 9 "parent_id":"2020", 10 "children":[ 11 { 12 "child_id":"100", 13 "checked":false 14 }, 15 { 16 "child_id":"200", 17 "checked":false 18 } 19 ] 20 }, 21 { 22 "parent_id":"2021", 23 "children":[ 24 { 25 "child_id":"3", 26 "checked":false 27 } 28 ] 29 }, 30 { 31 "parent_id":"2022", 32 "children":[ 33 { 34 "child_id":"103", 35 "checked":false 36 }, 37 { 38 "child_id":"104", 39 "checked":true 40 }, 41 { 42 "child_id":"105", 43 "checked":true 44 }, 45 { 46 "child_id":"106", 47 "checked":false 48 } 49 ] 50 } 51 ] 52}`).value; 53 54const IndeterminateCheckbox = ({ data: { parent_id, children }, onCheck }) => { 55 const handleChange1 = (event) => { 56 onCheck( 57 [...children].map((_, i) => i), 58 event.target.checked 59 ); 60 }; 61 62 const handleChange2 = (event, i) => { 63 onCheck([i], event.target.checked); 64 }; 65 66 const childrenBox = ( 67 <Box sx={{ display: "flex", flexDirection: "column", ml: 3 }}> 68 {children.map(({ child_id, checked }, i) => ( 69 <FormControlLabel 70 key={`child-${child_id}`} 71 label={`Child-${child_id}`} 72 control={ 73 <Checkbox 74 checked={checked} 75 onChange={(event) => { 76 handleChange2(event, i); 77 }} 78 /> 79 } 80 /> 81 ))} 82 </Box> 83 ); 84 85 return ( 86 <div> 87 <FormControlLabel 88 label={`Parent-${parent_id}`} 89 control={ 90 <Checkbox 91 checked={children.every(({ checked }) => checked)} 92 indeterminate={ 93 children.some(({ checked }) => checked) && 94 children.some(({ checked }) => !checked) 95 } 96 onChange={handleChange1} 97 /> 98 } 99 /> 100 {childrenBox} 101 </div> 102 ); 103}; 104 105const App = () => { 106 const [checkArray, setCheckArray] = React.useState(DEFAULT_ARRAY); 107 108 const handleCheck = React.useCallback( 109 (dataIndex, childrenIndexes, checked) => { 110 const nextCheckArray = checkArray.map((data, i) => { 111 if (i !== dataIndex) return data; 112 113 return { 114 ...data, 115 children: data.children.map((child, j) => ({ 116 ...child, 117 checked: childrenIndexes.includes(j) ? checked : child.checked 118 })) 119 }; 120 }); 121 122 setCheckArray(nextCheckArray); 123 }, 124 [checkArray] 125 ); 126 127 return ( 128 <div> 129 {checkArray.map((data, i) => ( 130 <IndeterminateCheckbox 131 key={`data-${i}`} 132 data={data} 133 onCheck={(childrenIndexes, checked) => { 134 handleCheck(i, childrenIndexes, checked); 135 }} 136 /> 137 ))} 138 </div> 139 ); 140}; 141 142export default App; 143 144 145

追記

上記回答コードの修正案を挙げておきます。AppのhandleCheckの引数を以下のように修正しました。

diff

1- (dataIndex, childrenIndexes, checked) => { 2+ (parentId, childId, checked) => {
  • 第一引数を、親要素のインデクスにしていたのを、親要素のidに変更した。

  • 第二引数を子要素のインデクスの配列にしていましたが、配列ではなく子要素のid一個にして、これに-1 を渡されると、すべての子要素のcheckedを第三引数で渡される値に更新することにした。

上記の変更にあわせて全体を見直したものがコレです???? forkしてそれっぽく動くようにしたもの(修正後)

投稿2021/11/13 17:56

編集2021/11/14 01:53
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

underfield

2021/11/14 12:42

サンプルを修正していただき、大変有り難うございます。 やりたいことの実現ができそうな目処が立ちました。 また、修正版までしていただき感謝しかありません。今後とも宜しくお願いします。
退会済みユーザー

退会済みユーザー

2021/11/14 15:09 編集

> やりたいことの実現ができそうな目処が立ちました。 よかったです???? 配列の各要素が、重複のない id を持っている場合、 ・更新などの対象になる要素を特定するとき や、 ・コンポーネントの配列をmapで作るときの、各コンポーネントのkey属性の値をつくるとき には、追記した修正案のように、配列内のインデクス(mapに与える関数の第二引数)ではなく、id を使ったほうが(後々の機能追加のことを考えても)より良いかと思います。
underfield

2021/11/29 00:25

サンプルをもとにやりたいことが実現できました。 ありがとうございます。またよろしくおねがいします。
guest

0

ある配列の特定の要素を更新する処理はArray.prototype.map()を使って以下のように書きます。

js

1const update = (arr, index, value) => 2 // mapに渡す関数で、i番目の要素ならvalue、それ以外はオリジナルを返すことで、配列を更新する 3 arr.map((v, i) => i === index ? value : v);

あるオブジェクトの特定のプロパティの値を更新する処理はスプレッド構文を使って以下のように書けます。

js

1const updateId = (obj, newId) => 2 // スプレッド構文でオブジェクトの中身を展開し、キーが"id"のプロパティの値はnewIdにすることで更新する 3 ({ ...obj, id: newId });

キー名も引数で指定したいときはこうです。

js

1const update = (obj, key, value) => 2 // スプレッド構文でオブジェクトの中身を展開し、キーがkeyのプロパティの値はvalueにすることで更新する 3 ({ ...obj, [key]: value });

よって、配列の中のi番目のオブジェクトのcheckedプロパティを更新する処理はこう書きます。

js

1const update = (arr, index, value = true) => 2 arr.map((item, i) => ({ 3 ...item, 4 checked: (i === index) ? value : item.checked, 5 })); 6 7const data = [{ 8 id: "a", 9 checked: false, 10}, { 11 id: "b", 12 checked: false, 13}]; 14 15const b_checked = update(data, 1); 16 17/* 18b_checked = [{ 19 id: "a", 20 checked: false, 21}, { 22 id: "b", 23 checked: true, // 2番目のオブジェクトのcheckedプロパティが更新される 24}]; 25 */

というわけで、これらを踏まえて考えてみると、以下のようになるのではないでしょうか。

parent_idchild_idを指定して更新するパターンと、配列のインデックスを指定して更新するパターンを作ってみました。

js

1// parent_idとchild_idを指定して更新 2const updateCheckById = (data, parentId, childId, value = true) => 3 data.map(parent => ({ 4 ...parent, 5 state: parent.state.map(child => ({ 6 ...child, 7 checked: (parent.parent_id === parentId && child.child_id === childId) ? value : child.checked, 8 })), 9 }));

js

1// インデックスを指定して更新 2const updateCheckByIndex = (data, parentIndex, childIndex, value = true) => 3 data.map((parent, parent_index) => ({ 4 ...parent, 5 state: parent.state.map((child, child_index) => ({ 6 ...child, 7 checked: (parent_index === parentIndex && child_index === childIndex) ? value : child.checked, 8 })), 9 }));

以上、お役に立てれば幸いです。

投稿2021/11/13 15:40

編集2021/11/13 16:41
fj68

総合スコア752

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

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

underfield

2021/11/14 12:44

細かく動作に関して、ご教授いただきありがとうござます。 大変わかりやすく助かりました。 ご回答頂きましたソースでも動作検証を行ってみようと思っております。 今後とも宜しくお願いします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.37%

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

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

質問する

関連した質問