Closed57

DartのListについて調べる

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

とりあえず使ってみる

void main() {
  final fixedLengthList = List<int>.filled(5, 0);
  print(fixedLengthList); // [0, 0, 0, 0, 0]
  fixedLengthList.add(0); // Uncaught Error: Unsupported operation: add
}

filledコンストラクタで作った場合は追加できない

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

[]を使うと可変にできる。

void main() {
  final growableList = [0, 0, 0, 0, 0];
  growableList.add(0);
  print(growableList); // [0, 0, 0, 0, 0, 0]
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

Listクラスには下記の7つのコンストラクタがある

  • List
  • empty
  • filled
  • from
  • generate
  • of
  • unmodifiable

一番最初のListは非推奨、代わりにリストリテラル[]やfilledを使おうとのこと

emptyは空のリストを作成する、省略可能なgrowableオプションを受け取る、デフォルトはfalse

void main() {
  final list = List<int>.empty(growable: true);
  list.add(1);
  print(list); // [1]
}

filledは先ほど見た通り

fromはイテレーターを受け取ってリストを作成する、イテレーターの型は任意

generateは長さとgenerator関数からリストを作成する、generatorの引数はint型のindex

ofはfromと似ているがイテレーターの型がリストの型と同じである必要がある

unmodifiableはfromと同様に任意の型のイテレーターを受け取る、長さや内容を一切変更できないリストを作成する

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

Listクラスには10個のプロパティがある、それぞれ見てみる

void main() {
  final list = [1, 2, 3, 4, 5];
  print(list.first);
  print(list.hashCode);
  print(list.isEmpty);
  print(list.isNotEmpty);
  print(list.iterator);
  print(list.last);
  print(list.length);
  print(list.reversed);
  print(list.runtimeType);
  print(list.single);
}

実行結果

1
795075260
false
true
Instance of 'ArrayIterator<int>'
5
5
(5, 4, 3, 2, 1)
JSArray<int>
Uncaught Error: Bad state: Too many elements

reservedはリストと思いきや[]でないので違うようだ

runtimeTypeはJSArray<int>List<int>ではない

singleは要素が一つならその要素そのものを返すが、それ以外の場合はStateError例外を発生させる

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

addAllメソッドを使うと複数の要素を追加できる、引数は同じ型のイテレータ

void main() {
  final list = [1];
  list.addAll([2, 3]);
  print(list); // [1, 2, 3]
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

anyメソッドを使うと指定した条件を満たす要素が含まれるかをチェックできる

void main() {
  final list = [1, 2, 3];
  print(list.any((n) => n == 3)); // true
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

asMapメソッドを使うとリストをマップに変換できる、インデックスがキーとなる

void main() {
  final list = [1, 2, 3];
  print(list.asMap()); // {0: 1, 1: 2, 2: 3}
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

cast<R>メソッドを使うとList<R>にキャストできる、要素がRのインスタンスである必要がある

例えば下記のようなのはダメ

void main() {
  final list = [1, 2, 3];
  print(list.cast<String>()); // Uncaught Error: TypeError: 1: type 'JSInt' is not a subtype of type 'String'
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

containsメソッドを使うとある要素が含まれるかをチェックできる

void main() {
  final list = [1, 2, 3];
  print(list.contains(2)); // true
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

elementAtを使うとn番目の要素を取得できる、nは0以上でリストの長さより小さい必要がある

void main() {
  final list = [1, 2, 3];
  print(list.elementAt(2)); // 3
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

everyメソッドを使うとリスト内の全ての要素が指定の条件を満たすか否かをチェックできる

void main() {
  final list = [1, 2, 3];
  print(list.every((n) => 1 <= n && n <= 3)); // true
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

expandメソッドを使うとコレクションにMapしたものをJoinできるイメージ

void main() {
  final list = [1, 2, 3];
  print(list.expand((n) => [n, n])); // (1, 1, 2, 2, 3, 3)
}

どういうときに使うんだろう

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

fillRange

開始位置から終了位置までに指定値を代入できる

void main() {
  final list = List.filled(5, 0);
  list.fillRange(1, 4, 1);
  print(list); // [0, 1, 1, 1, 0]
}

リストがNullableの場合は第3引数を省略可能、その場合はnullとなる

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

firstWhere

条件を満たす最初の要素を取得できる

void main() {
  final list = [1, 2, 3, 4, 5];
  final first = list.firstWhere((n) {
    return n >= 3 && n % 2 == 0;
  });

  print(first); // 4
}

すべてが条件を満たさない場合はIterableElementError.noElement 例外が発生する

第2引数のオプションでorElseを指定することでデフォルト値を指定できる

void main() {
  final list = [1, 2, 3, 4, 5];
  final first = list.firstWhere((n) {
    return n >= 5 && n % 2 == 0;
  });

  print(first); // Uncaught Error: Bad state: No element
}
void main() {
  final list = [1, 2, 3, 4, 5];
  final first = list.firstWhere(
    (n) {
      return n >= 5 && n % 2 == 0;
    },
    orElse: () => -1,
  );

  print(first); // -1
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

fold

JavaScriptでいうところのreduce、左畳み込み

void main() {
  final list = [1, 2, 3, 4, 5];
  final first = list.fold(0, (memo, n) => memo + n);
  print(first); // 15
}

ちなみに右畳み込みもあるらしい

https://thinkng.dev/collectionx.dart/iterables/IterableExt/foldRight.html

実装を見ると無限長のリストとかは扱えなそう

S foldRight<S>(S initial, ReversedAccumulate<S, E> f) {
  var acc = initial;
  asList().reversed.forEach((e) => acc = f(e, acc));
  return acc;
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

followedBy

リストの末尾に指定のイテラブルを連結したイテラブルを作成する

void main() {
  final list = [1, 2];
  print(list.followedBy([3, 4])); // (1, 2, 3, 4)
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

getRange

リスト内の指定範囲からイテラブルを作成する

void main() {
  final list = [1, 2, 3, 4, 5];
  print(list.getRange(1, 4)); // (2, 3, 4)
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

indexOf

指定値が最初に出現するインデックスを返す、無ければ-1を返す

void main() {
  final list = [1, 2, 3, 4, 5];
  print(list.indexOf(3)); // 2
  print(list.indexOf(6)); // -1
}

第2引数として開始位置を指定できる

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

indexWhere

指定の条件を満たす最初の要素のインデックスと返す、無ければ-1を返す

void main() {
  final list = [1, 2, 3, 4, 5];
  print(list.indexWhere((n) => n == 3)); // 2
  print(list.indexWhere((n) => n >= 6)); // -1
}

第2引数として開始位置を指定できる

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

insert

指定位置に要素を挿入する

void main() {
  final list = [1, 3];
  list.insert(1, 2);
  print(list); // [1, 2, 3]
}

指定位置が不正だとRangeError例外が発生する

void main() {
  final list = [1, 3];
  list.insert(100, 2);
  print(list); // Uncaught Error: RangeError: Value not in range: 100
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

join

JavaScriptのjoinと同様、指定文字列で区切って要素同士を連結する

void main() {
  final list = [1, 2, 3];
  print(list.join(",")); // 1,2,3
}

区切り文字列に何も指定しない場合は空文字列""が指定されたものとみなされる

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

lastIndexOf, lastIndexWhere, lastWhere

それぞれindexOf, indexWhere, firstWhereの末尾からバージョンなので割愛

第2引数は末尾からのインデックスではなく、先頭からのインデックスなので注意

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

map

要素に指定関数を適用して新しいイテラブルを作成する

void main() {
  final list = [1, 2, 3];
  print(list.map((n) => n * n)); // (1, 4, 9)
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

reduce

左畳み込み、foldと似ているが少し違う

  • 初期値が最初の要素
  • 初期値がスキップされる
void main() {
  final list = [1, 2, 3, 4, 5];
  print(list.reduce((memo, n) => memo + n)); // 15
}

リファレンスの下記の下記の擬似コードがわかりやすい

E value = iterable.first;
iterable.skip(1).forEach((element) {
  value = combine(value, element);
});
return value;

https://api.dart.dev/be/180791/dart-core/Iterable/reduce.html

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

remove系

下記5点のメソッド、すべて可変長かつ非空のリストである必要がある

  • remove:指定値と同じである最初の要素を取り除く、成功の場合はtrueを返す
  • removeAt:指定位置の要素を取り除く、取り除いた要素を返す
  • removeLast:末尾の要素を取り除く、取り除いた要素を返す
  • removeRange:指定範囲のすべての要素を取り除く、何も返さない
  • removeWhere:指定の条件を満たすすべての要素を取り除く、何も返さない
void main() {
  final list = [1, 2, 3, 4, 5];
  print(list.remove(1)); // true
  print(list); // [2, 3, 4, 5]
  print(list.removeAt(0)); // 2
  print(list); // [3, 4, 5]
  print(list.removeLast()); // 5
  print(list); // [3, 4]
  list.removeRange(0, 1);
  print(list); // [4]
  list.removeWhere((n) => n == 4);
  print(list); // []
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

retainWhere

指定の条件を満たす要素以外を取り除く、removeWhereの反対

void main() {
  final list = [1, 2, 3, 4];
  list.retainWhere((n) => n % 2 == 1);
  print(list); // [1, 3]
}

ちなみにremoveWhereの場合

void main() {
  final list = [1, 2, 3, 4];
  list.removeWhere((n) => n % 2 == 1);
  print(list); // 2, 4
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

setAll

指定位置から要素を上書きしていく

void main() {
  final list = [1, 2, 3, 4];
  list.setAll(1, [3, 5, 7]);
  print(list); // [1, 3, 5, 7]
}

第2引数のイテラブルの要素数とリストの要素数の大小関係に注意する必要がある

void main() {
  final list = [1, 2, 3, 4];
  list.setAll(1, [3, 5, 7, 9]);
  print(list); // Uncaught Error: RangeError (index): Index out of range: index should be less than 4: 4
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

setRange

指定範囲の要素を上書きする

void main() {
  final list = [1, 2, 3, 4];
  list.setRange(1, 4, [3, 5, 7, 9]);
  print(list); // [1, 3, 5, 7]
}

イテラブルの数がリストの要素数をオーバーしても大丈夫、でも少ないのはダメ

void main() {
  final list = [1, 2, 3, 4];
  list.setRange(1, 4, [3, 5]);
  print(list); // Uncaught Error: Bad state: Too few elements
}

ちなみに第3引数でイテラブルの開始位置を指定できる

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

singleWhere

条件を満たす最初の要素を返す、無ければ例外が発生するかorElseオプションが呼び出される

void main() {
  final list = [1, 2, 3, 4];
  final first = list.singleWhere((n) => n >= 4);
  print(first); // 4
}

firstWhereに似ているが複数の要素が条件を満たすと例外が発生する

void main() {
  final list = [1, 2, 3, 4];
  final first = list.singleWhere((n) => n >= 3);
  print(first); // Uncaught Error: Bad state: Too many elements
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

skip

指定数の要素をスキップしてイテラブルを作成する

void main() {
  final list = [1, 2, 3, 4];
  print(list.skip(2)); // (3, 4)
}

takeと組み合わせるとJavaScriptのsliceが実現できそう

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

skipWhile

指定の条件が満たされなくなるまでスキップしてイテラブルを作成する

void main() {
  final list = [1, 2, 3, 4];
  print(list.skipWhile((n) => n <= 2)); // (3, 4)
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

sublist

なんとJavaScriptのsliceのようなものがあるではないか

void main() {
  final list = [1, 2, 3, 4];
  print(list.sublist(1, 3)); // [2, 3]
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

take

先頭から指定した数の要素を取ってイテラブルを作成する

void main() {
  final list = [1, 2, 3, 4];
  print(list.take(2)); // (1, 2)
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

takeWhile

先頭から指定した条件を満たす限り要素を取ってイテラブルを作成する

void main() {
  final list = [1, 2, 3, 4];
  print(list.takeWhile((n) => n <= 2)); // (1, 2)
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

toList

リストに変換する、同じ内容の新しいリストを作成する時などに便利そう

void main() {
  final list = [1, 2, 3, 4];
  print(list.toList()); // [1, 2, 3, 4]
}

第1引数growableがtrueならリストは可変長となる、デフォルトではtrue

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

where

指定の条件を満たす全ての要素をイテラブルとして返す

void main() {
  final list = [1, 2, 3, 4];
  print(list.where((n) => n % 2 == 0)); // (2, 4)
}

retainWhereと似ているが戻り値がvoidではない点が異なる

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

whereType

指定の型を持つ全ての要素をイテラブルとして返す

void main() {
  final list = [1, "2", 3, "4"];
  print(list.whereType<String>()); // (2, 4)
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

operator ==

同じリストかどうかをする、同じ要素を含んでいても同一とは見なされない

void main() {
  final list1 = [1, 2];
  final list2 = [1, 2];
  final list3 = const [1, 2];
  final list4 = const [1, 2];

  print(list1 == list2); // false
  print(list3 == list4); // true
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

List.castFrom<S, T>

指定リストの要素の型をSからTにキャストする、T is-a Sの関係が必要

void main() {
  final list = [1, 2];
  print(List.castFrom<num, String>(list)); // Uncaught Error: TypeError: 1: type 'JSInt' is not a subtype of type 'String'
}
薄田達哉 / tatsuyasusukida薄田達哉 / tatsuyasusukida

List.copyRange

リストの指定位置に別のリストの要素をコピーする

void main() {
  final list = [1, 2, 3, 4, 5];
  List.copyRange(list, 1, [-1, -2, -3]);
  print(list); // [1, -1, -2, -3, 5]
}

第4引数と第5引数とコピーする範囲を指定できる

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

List.writeIterable

リストの指定位置にイテラブルの要素をコピーする

void main() {
  final list = [1, 2, 3, 4, 5];
  List.writeIterable(list, 1, {-1, -2, -3});
  print(list); // [1, -1, -2, -3, 5]
}

第2引数がリストの場合はList.copyRangeを使った方が効率的

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