Closed34

DartのMapについて調べる

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

Mapリテラル

MapリテラルはJavaScriptオブジェクトど同様だが、キーに文字列以外も使える

void main() {
  final map = {
    "one": 1,
    "two": 2,
  };
  print(map); // {one: 1, two: 2}
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

値のアクセス

operator []を使って値にアクセスできる、存在しないキーの場合はnullが返される

void main() {
  final map = {
    "one": 1,
    "two": 2,
  };
  print(map["one"]); // 1
  print(map["two"]); // 2
  print(map["three"]); // null
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

値の上書きと追加

operator []=を使って値の上書きができる、存在しないキーの場合は追加される

void main() {
  final map = {
    "one": 1,
    "two": 2,
  };
  
  map["one"] = -1;
  map["two"] = -2;
  map["three"] = -3;
  
  print(map); // {one: -1, two: -2, three: -3}
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

Mapの長さ

lengthプロパティを使ってMapの長さ(key-valueペアの数)を取得できる

void main() {
  final map = {
    "one": 1,
    "two": 2,
  };
  
  print(map.length); // 2
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

スプレッド

operator ...を使ってマップを別のマップに展開できる

void main() {
  final map1 = {
    "one": 1,
    "two": 2,
  };
  
  final map2 = {
    ...map1,
    "three": 3,
  };
  
  print(map2); // {one: 1, two: 2, three: 3}
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

制御フロー

リテラル内でifやforが使える

void main() {
  final map = {
    if (true) "one": 1,
    if (false) "two": 2,
    for (var i = 3; i <= 5; i += 1) i.toString(): i,
  };
  
  print(map); // {one: 1, 3: 3, 4: 4, 5: 5}
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

定数

Mapリテラルにconstを前置すると定数になる

void main() {
  final map1 = {1: 1};
  final map2 = {1: 1};
  final map3 = const {1: 1};
  final map4 = const {1: 1};
  
  print(map1 == map2); // false
  print(map3 == map4); // true
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

Map.fromコンストラクタ

他のマップからマップを作成する、型は異なっても許容されるがランタイムエラー発生の恐れがある

void main() {
  final map1 = <dynamic, dynamic>{
    "one": 1,
    "two": 2,
  };
  
  final map2 = Map<String, int>.from(map1);
  
  print(map2); // {one: 1, two: 2}
}

Map.ofコンストラクタの方が型チェックが静的に行われるので望ましい

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

Map.fromEntriesコンストラクタ

MapEntryのイテラブルからMapを作成する

void main() {
  final entry = MapEntry<String, int>("one", 1);
  final map = Map.fromEntries([entry]);

  print(entry); // MapEntry(one: 1)
  print(map); // {one: 1}
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

Map.fromIterablesコンストラクタ

keyのイテラブルとvalueのイテラブルからMapを作成する

void main() {
  final keys = ["one"];
  final values = [1];
  final map = Map.fromIterables(keys, values);

  print(map); // {one: 1}
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

Map.identityコンストラクタ

キーの比較に下記が使用されるらしい

  • Object.== → identical
  • Object.hashCode → identityHashCode

現時点では謎、試行錯誤のソースコードを備忘のため残しておく

void main() {
  final map = Map<List, int>.identity();
  final key = const [];
  final value = 1;

  map[key] = value;
  
  print(identical(key, []));
  print(identical(key, key));

  print(key == []);
  print(key == key);

  print(map[key]);
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

Map.ofコンストラクタ

他のマップからマップを作成する、型が同じである必要がある、異なる場合はコンパイルエラー表示

void main() {
  final map1 = <dynamic, dynamic>{
    "one": 1,
    "two": 2,
  };
  
  final map2 = Map<String, int>.of(map1);
  
  print(map2); // {one: 1, two: 2}
}

エラーメッセージは下記の通り

The argument type 'Map<dynamic, dynamic>' can't be assigned to the parameter type 'Map<String, int>'.
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

Map.unmodifiableコンストラクタ

マップから書き込みできないマップを作成する

void main() {
  final map1 = Map<String, int>.unmodifiable({
    "one": 1,
  });
  
  print(map1); // {one: 1}
  map1["two"] = 2; // Uncaught Error: Unsupported operation: Cannot modify unmodifiable Map
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

Mapのプロパティ

マップには下記の8つのプロパティがある

  • entries: MapEntryのイテラブル
  • hashCode: Mapのハッシュコード、中身が同じでも異なる
  • isEmpty: 空ならばtrue
  • isNotEmpty: 空で無ければtrue
  • keys: keyのイテラブル
  • length: エントリーの数
  • runtimeType: ランタイムの型
  • values: valueのイテラブル
void main() {
  final map1 = {"one": 1};
  final map2 = {"one": 1};
  
  print(map1.entries); // (MapEntry(one: 1))
  print(map1.hashCode); // 325431624
  print(map2.hashCode); // 264261406
  print(map1.isEmpty); // false
  print(map1.isNotEmpty); // true
  print(map1.keys); // (one)
  print(map1.length); // 1
  print(map1.runtimeType); // JsLinkedHashMap<String, int>
  print(map1.values); // (1)
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

allAllメソッド

同じ型のMapのエントリーを全て追加する

void main() {
  final map1 = {"one": 1};
  final map2 = {"two": 2, "three": 3};
  
  map1.addAll(map2);
  print(map1); // {one: 1, two: 2, three: 3}
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

addEntriesメソッド

同じ型のMapEntryのイテラブルを全て追加する

void main() {
  final map = {"one": 1};
  final entries = [
    MapEntry("two", 2),
    MapEntry("three", 3),
  ];
  
  map.addEntries(entries);
  print(map); // {one: 1, two: 2, three: 3}
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

castメソッド

Map<RK, RV>型にキャストする、K is RKかつV is RVである必要がある

void main() {
  final map = {"one": 1};
  
  print(map.cast<Object, Object>()); // {one: 1}
  print(map.cast<String, String>()); // Uncaught Error: TypeError: 1: type 'JSInt' is not a subtype of type 'String'
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

forEachメソッド

Mapを走査する、keyとvalueの2引数関数を受け取る

void main() {
  final map = {"one": 1};
  map.forEach((key, value) => {
    print("key = $key, value = $value");
  }); // key = one, value = 1
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

mapメソッド

Mapを別の型のMapにマッピングする、MapEntryを返す2引数関数を受け取る

void main() {
  final map = {"one": 1};
  final mapped = map.map((key, value) {
    return MapEntry(value, key);
  });
  
  print(mapped); // {1: "one"}
}

MapEntryで型推論されるのでmapメソッドへの型引数の指定は不要

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

putIfAbsentメソッド

Mapに指定のkeyが存在しなければvalueを追加する、valueは関数で指定する

void main() {
  final map = {"one": 1};
  map.putIfAbsent("one", () => -1);
  map.putIfAbsent("two", () => 2);

  print(map); // {one: 1, two: 2}
}

戻り値はvalue

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

removeメソッド

Mapから指定キーを削除して返す、無ければMapには変更を加えずにnullを返す

void main() {
  final map = {"one": 1};
  map.remove("one");
  print(map); // {}
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

removeWhereメソッド

指定の条件を満たすエントリーを削除する

void main() {
  final map = {"one": 1, "two": 2, "three": 3};
  map.removeWhere((k, v) => v % 2 == 1);
  print(map); // {two: 2}
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

toStringメソッド

Mapを文字列表現に変換する

void main() {
  final map = {"one": 1, "two": 2, "three": 3};
  print(map.toString()); // {one: 1, two: 2, three: 3}
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

updateメソッド

Mapの指定のキーを更新する、第2引数として値の型を受け取って返す関数を受け取る

void main() {
  final map = {"count": 0};
  map.update("count", (count) => count + 1);
  print(map); // {count: 1}
}

第3引数ifAbsentとしてvalueを返す関数を受け取る、キーが存在しない場合に呼び出される

void main() {
  final map = {"one": 1};
  map.update("count", (count) => count + 1, ifAbsent: () => 0);
  print(map); // {one: 1, count: 0}
}

キーが存在しない場合はifAbsentが無ければエラーになる

void main() {
  final map = {"one": 1};
  map.update("count", (count) => count + 1); // Uncaught Error: Invalid argument (key): Key not in map.: "count"
  print(map);
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

updateAllメソッド

Mapの全てのエントリを更新する、valueを返す2引数関数を受け取る

void main() {
  final map = {"one": 1, "two": 2};
  map.updateAll((k, v) => v * -1);
  print(map); // {one: -1, two: -2}
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

オペレーター

Mapには下記3つのオペレーターがある

  • operator ==
  • operator []
  • operator []=

検証についてLanguage tourと重複する部分があるので割愛する

薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

Map.castFrom

マップをキャストする

void main() {
  final map = {"one": 1, "two": 2};
  final casted = Map.castFrom<String, int, Object, Object>(map);
  print(casted); // {one: 1, two: 2}
}

キャスト前後のkeyとvalueの型にはis-a関係がある必要がある

このスクラップは2023/01/10にクローズされました