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

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

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

Flutterは、iOSとAndroidのアプリを同じコードで開発するためのフレームワークです。オープンソースで開発言語はDart。双方のプラットフォームにおける高度な実行パフォーマンスと開発効率を提供することを目的としています。

Dart

Dartは、Googleによって開発されたJavaScriptの代替となることを目的に作られた、ウェブ向けのプログラミング言語である。

Q&A

解決済

1回答

4528閲覧

Dart/Flutterにおける複数階層にネストしたmapの扱い方

neoz

総合スコア31

Flutter

Flutterは、iOSとAndroidのアプリを同じコードで開発するためのフレームワークです。オープンソースで開発言語はDart。双方のプラットフォームにおける高度な実行パフォーマンスと開発効率を提供することを目的としています。

Dart

Dartは、Googleによって開発されたJavaScriptの代替となることを目的に作られた、ウェブ向けのプログラミング言語である。

0グッド

0クリップ

投稿2020/09/17 09:13

前回まではダミーデータで動かしていたのですが、DBの構造が決まったのでそちらのデータ構造でコードを動かすことになりました。

目的は、以下のallDataからweightやfoodのDataやWeightを出力することです。

Dart

1var allData = 2[ 3 { key: 2020-09-12=21:36:25:027, 4 data: 5 { name: Jim, 6 sex: Male, 7 weight: 8 { -乱数: 9 { Date: 2020-9-6, 10 Weight: 5.1, 11 }, 12 -乱数: 13 { Date: 2020-9-9, 14 Weight: 5.4, 15 }, 16 }, 17 food: 18 { -乱数: 19 { Data:***, 20 Item:***, 21 }, 22 }, //項目続く

前回のデータ構造では以下の内容でうまくデータを取り出すことができたのですが、

Dart

1for (int i = 0; i < allData.length; i++) { 2 if (allData[i]['data']['name'] != widget.name) continue; 3 4 final weightData = 5 (allData[i]['data'] as Map<String, Map<String, Map>>)['weight']; 6 7 print(weightData); 8 9 for (int j = 0; j < weightData.length; j++) { 10 data.add( 11 DataPoint<DateTime>( 12 value: weightData[j]['value'].toDouble(), 13 xAxis: parseDateString(weightData[j]['dateTime']), 14 ), 15 ); 16 } 17 } 18 data.forEach(print); 19 }

今回のデータ構造だと新たに2つ問題が出てきました。

  1. 上記コードがtype '_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, Map<String, Map<dynamic, dynamic>>>' in type castと言われ動作しない。

私がこのcastの原則を理解していないので応用が効かず恐縮なのですが、今回の場合どのように書くべきでしょうか?

  1. この全体構造だとしても、weightやfoodのデータを取ってくるには、別の質問でご回答頂いたfor inでweightを回して、keysを利用すれば良いでしょうか?

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

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

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

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

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

guest

回答1

0

ベストアンサー

Map型からvalueの部分だけを取り出すには、valuesという便利なプロパティが使えます。

dart

1 for (int i = 0; i < allData.length; i++) { 2 final info = allData[i]['data'] as Map<String, dynamic>; 3 if (info['name'] != widget.name) continue; 4 5 final weightData = info['weight'] as Map<String, Map>; 6 for (var value in weightData.values) { 7 data.add( 8 DataPoint<DateTime>( 9 value: value['value'].toDouble(), 10 xAxis: parseDateString(value['dateTime']), 11 ), 12 ); 13 } 14 }

また、根本的な問題になりますが、このようなネストしたデータは、クラスに変換して扱った方が良いです。

どんなアプリを作っているか分からないので大体の雰囲気だけ示すと、以下のような感じです。

dart

1 2// ユーザーを表すクラス 3class User { 4 final String key; 5 final String name; 6 final Sex sex; 7 final List<WeightData> weightHistory; 8 9 User({ 10 this.key, 11 this.name, 12 this.sex, 13 this.weightHistory, 14 }); 15} 16 17// 体重データを表すクラス 18class WeightData { 19 final double value; 20 final DateTime date; 21 22 WeightData({ 23 this.value, 24 this.date, 25 }); 26} 27 28// 性別を表すEnum 29enum Sex { 30 Male, 31 Female, 32 Unknown, 33} 34 35// これらを使うと、データはこんな感じで表現できる 36final List<User> allData = [ 37 User( 38 key: '1', 39 name: 'Jim', 40 sex: Sex.Male, 41 weightHistory: [ 42 WeightData( 43 date: DateTime(2020, 8, 25), 44 value: 5.1, 45 ), 46 WeightData( 47 date: DateTime(2020, 8, 26), 48 value: 5.4, 49 ), 50 ], 51 ), 52 User( 53 key: '2', 54 name: 'Tom', 55 sex: Sex.Male, 56 weightHistory: [ 57 WeightData( 58 date: DateTime(2020, 8, 25), 59 value: 5.1, 60 ), 61 WeightData( 62 date: DateTime(2020, 8, 26), 63 value: 5.4, 64 ), 65 ], 66 ), 67]; 68 69// 実際の使用例 70class ExamplePage extends StatefulWidget { 71 final String name; 72 73 const ExamplePage({ 74 Key key, 75 this.name, 76 }) : super(key: key); 77 78 79 _ExamplePageState createState() => _ExamplePageState(); 80} 81 82class _ExamplePageState extends State<ExamplePage> { 83 List<DataPoint<DateTime>> data; 84 85 86 void initState() { 87 super.initState(); 88 this.data = allData 89 .where((user) => user.name == widget.name) 90 .map((user) => user.weightHistory) 91 .expand((data) => data) 92 .map( 93 (data) => DataPoint<DateTime>( 94 value: data.value, 95 xAxis: data.date, 96 ), 97 ) 98 .toList(); 99 } 100 101 102 Widget build(BuildContext context) { 103 return Scaffold( 104 body: Center( 105 child: Text('${data[0]}'), 106 ), 107 ); 108 } 109} 110

投稿2020/09/17 09:22

編集2020/09/17 13:44
nskhei

総合スコア704

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

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

neoz

2020/09/17 09:47

ありがとうございます。すみません、以前のコードは final weightData = (allData[i]['data'] as Map<String, List>)['weight']; でした。 今回、構造が変わってその部分がうまく読み込めなくなったので、私が書いたのがMap<String, Map<String, Map>>でしたが、これですと type '_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, Map<String, Map<dynamic, dynamic>>>' in type cast と言われてしまいます。 この部分はどうcastするのが正しいでしょうか?
nskhei

2020/09/17 10:31

コード修正しました。ちょっと変数名が微妙ですが。
neoz

2020/09/17 11:46

ありがとうございます、しかし未だに同じエラーがでます。。これはなぜなのでしょうか。 type '_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, dynamic>' in type cast 一応、for文の前で以下をプリントすると、正しく出力はするので、データ自体の取得は問題なさそうです。 print('${allData[0]['data']['weight']['-乱数']}');
nskhei

2020/09/17 12:41

すいません、これ以上は自分には分かりそうにないです...。 申し訳ないですが、他の回答を待ってみてください。 あと蛇足ですが、Dartではここまでネストしたデータをそのまま扱うことはありません。 通常は、DBからデータを取得した後、一度モデルクラスに変換してから扱います。 なので、普通はこのようなキャストエラー地獄は起きません。 DBの実装が出来たら、一度その辺について調べて設計から見直してみることをオススメします。
neoz

2020/09/17 13:10

わかりました、ありがとうございました! よく調べてみようと思いますが、今回のものをモデルクラスに変換するとどのようになるでしょうか・・?
nskhei

2020/09/17 13:47

回答の方に追記しました。 元データを変換するコードは別途必要になります。
neoz

2020/09/17 15:17

ありがとうございます!こちらを参考にして作ろうと思いましたが、理解していないままでは作れそうにないので、先程の道を進めてみて、あと一歩まで来ましたが、小数への変換がおかしなことになったので、また別質問とさせていただきます。 こちらの方法は別途じっくり勉強しようと思います。本旨とそれますが、何かDart/Flutterをこういったところから体系的に学べるサイト(日・英)や書籍等おすすめがありましたら、教えて頂けるとありがたいです。
neoz

2020/09/19 01:38

ありがとうございます、とても網羅的にまとまっていて勉強になりそうです。やってみます! また、掲題の件は、 final weightData = Map<String, dynamic>.from(allData[i]['data']['weight']); としたらcastエラーがでなくなり解決しました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問