dart collection 库(2)

下面是 dart collection 库对一些已有类型的扩展。

ComparatorExtension

extension ComparatorExtension<T> on Comparator<T>

var list = ['ab', 'cd', 'def'];  
comparator(String a, String b){ // 按长度排序  
  return b.length-a.length;  
}  
print([...list]..sort(comparator)); // [def, ab, cd] 正常排序  
print([...list]..sort(comparator.inverse)); // [ab, cd, def],逆序  
  
comparator2(String a, String b){ // 字符串按 ASCII 编码排序  
  return b.compareTo(a);  
}  
print([...list]..sort(comparator.then(comparator2))); // [def, cd, ab],如果第一个比较器两个相等的话,接着用后面的比较器  
  
var list2 = ['c', 'f'];  
var list3 = ['b', 'gxyz'];  
var list4 = [list,list2,list3];  
Comparator<List> comparator3 = comparator.compareBy((List l){ // compartor3 变量范型和参数范型对应  
  return l.join(); // 返回值类型是 comparator 的范型  
});  
// list4 的元素是 List 类型,先将它用 compareBy 那参数转换成字符串 ‘defcdab’ ‘cf', 'bgxyz'
// 然后再用 comparator 去比较三个字符串的长度  
list4.sort(comparator3); // [[def, cd, ab], [b, gxyz], [c, f]]

IterableExtension

extension IterableExtension<T> on Iterable<T>

属性

过滤方法

Iterable iterable = {'ab', 'def', 'Za', true, 1, 4.5, 2, false};  
iterable.sample(3); // [true, false, 4.5],随机取出 3 个元素

iterable.whereNot((value)=>value is bool); // (ab, def, Za, 1, 4.5, 2),不是 bool 类型的值

iterable.whereIndexed((index, element) {  
  return index > 2 && element.toString().length>2;  
}); // (true, 4.5, false)

iterable.whereNotIndexed((index, element) {  
  return index > 2 && element.toString().length>2;  
}); // (ab, def, Za, 1, 2) 索引小于 2 或者长度小于等于 2

iterable.firstWhereOrNull((element)=>element.toString().length==3); // def  
iterable.firstWhereIndexedOrNull((index, element)=>element.toString().length==7); // null
iterable.lastWhereOrNull((element)=>element.toString().length==3); // 4.5
iterable.singleWhereOrNull((element)=>element.toString().length==3); // false,因为有两个值长度都是 3

iterable.none((e)=>e is String); // false

排序方法

var sorted = iterable.sorted((v1, v2)=>v1.toString().compareTo(v2.toString()));  
print(iterable); // {ab, def, Za, true, 1, 4.5, 2, false},原集合顺序不变  
print(sorted); // [1, 2, 4.5, Za, ab, def, false, true]  
  
bool isSorted = sorted.isSorted((v1, v2)=>v1.toString().compareTo(v2.toString()));  
print(isSorted); // true  
  
keyOf(t)=>t.toString().length; // 定义一个转换函数 keyOf
// 先将元素转换成字符串长度,然后底层给两个元素排序用元素的 compareTo 方法,也不修改原集合顺序  
var sortedBy = iterable.sortedBy<num>(keyOf);  
print(sortedBy); // [1, 2, ab, Za, def, 4.5, true, false]  
print(sortedBy.isSortedBy<num>(keyOf)); // true  
  
var myComparator = ((num a, num b)=>a.compareTo(b)).inverse; // 自定义一个比较器  
var sortedByCompare = iterable.sortedByCompare<num>(keyOf, myComparator);  
print(sortedByCompare); // [false, true, def, 4.5, ab, Za, 1, 2]  
print(sortedByCompare.isSortedByCompare(keyOf, myComparator));

遍历方法

iterable.forEachIndexed((index, element)=>print('$index is $element'));  
iterable.forEachWhile((element) { // 遍历到 true 时结果是 false,就不遍历了,注意 true 这个不符合的也遍历到了  
  print(element.toString());  
  return element is String;  
});
iterable.forEachIndexedWhile((index, element) {
  print('$index is $element');
  return element is String;
});
iterable.mapIndexed((index, element) {  
  return '$index->$element';  
}); // (0->ab, 1->def, 2->Za, 3->true, 4->1, 5->4.5, 6->2, 7->false)

累积方法

iterable.expandIndexed((index, element) {  
  return [element, '$element->$index'];  
}); // (ab, ab->0, def, def->1, Za, Za->2, true, true->3, 1, ..., false, false->7)

// abdef[1]Za[2]true[3]1[4]4.5[5]2[6]false[7]
iterable.reduceIndexed((index, previous, element)=>'$previous$element[$index]');
// start===ab[0]def[1]Za[2]true[3]1[4]4.5[5]2[6]false[7]  
iterable.foldIndexed('start===', (index, previous, element)=>'$previous$element[$index]');

分组方法

var map = iterable.lastBy((t)=>t.toString().length);  
print(map); // {2: Za, 3: 4.5, 4: true, 1: 2, 5: false},2 一开始的值是 ab,然后被后面的 za 覆盖  
  
// 先用 keyOf 将所有的值转成函数第一个范型 int 的值,如果不同的值转换后结果一样的话,这些值要组合在一起  
// 用第二个函数 combine 组合,combine 的第一个参数的范型是 groupFoldBy 的第二个范型,表示相同的 key 的值组合成什么结构  
// 第二个参数的值是原来集合元素的类型,当前例子是 dynamic 的,函数返回类型是第一个参数的类型  
combine(String? previous, element) => previous==null?element.toString():'$previous-${element.toString()}';  
var map2 = iterable.groupFoldBy<int, String>(keyOf, combine);  
print(map2); // {2: ab-Za, 3: def-4.5, 4: true, 1: 1-2, 5: false},组合成字符串  
  
var map3 = iterable.groupFoldBy<int, Set<String>>(keyOf, (Set<String>? previous, element) => previous??<String>{}..add(element.toString()));  
print(map3); // {2: {ab, Za}, 3: {def, 4.5}, 4: {true}, 1: {1, 2}, 5: {false}},相同的 key 组合成  set  

var map4 = iterable.groupSetsBy(keyOf); // 内部指定了,就用 Set 去组合,groupFoldBy 的特例  
print(map4); // {2: {ab, Za}, 3: {def, 4.5}, 4: {true}, 1: {1, 2}, 5: {false}}  
  
var map5 = iterable.groupListsBy(keyOf); // 用 List 去组合  
print(map5); // {2: [ab, Za], 3: [def, 4.5], 4: [true], 1: [1, 2], 5: [false]}

分块方法

// 分块,参数的函数为 true 的元素作为分界点,作为块的第一个元素  
iterable.splitBefore((t)=>t.toString().length==2); // ([ab, def], [Za, true, 1, 4.5, 2, false])  
iterable.splitBefore((t)=>t.toString().length==3); // ([ab], [def, Za, true, 1], [4.5, 2, false])  
// test 函数用到了 index,只有大于 2 才去判断长度是否等于 3,否则都是 false
// 所以 index <= 2 的情况肯定都在第一个块中  
iterable.splitBeforeIndexed((index, t)=>index>2?t.toString().length==3:false); // ([ab, def, Za, true, 1], [4.5, 2, false])  
  
// test 为 true 的作为块的最后一个元素  
iterable.splitAfter((t)=>t.toString().length==2); // ([ab], [def, Za], [true, 1, 4.5, 2, false])  
iterable.splitAfter((t)=>t.toString().length==3); // ([ab, def], [Za, true, 1, 4.5], [2, false])  
iterable.splitAfterIndexed((index, t)=>index>2?t.toString().length==3:false); // ([ab, def, Za, true, 1, 4.5], [2, false])  

// 前一个比后一个长的时候,从中间分隔开  
iterable.splitBetween((first,second)=>first.toString().length>second.toString().length); // ([ab, def], [Za, true], [1, 4.5], [2, false])  
// 如果在 3-4 的索引满足条件才分割  
iterable.splitBetweenIndexed((index, first, second){ // ([ab, def, Za, true], [1, 4.5, 2, false])  
  if(index>2 && index<5) {  
    return first.toString().length>second.toString().length;  
  } else {  
    return false;  
  }  
});  

iterable.slices(3); // ([ab, def, Za], [true, 1, 4.5], [2, false])

其它方法

IterableComparableExtension

extension IterableComparableExtension<T extends Comparable<T>> on Iterable<T>

相当于 IterableExtension 的特例,集合的元素要是可比较的。

IterableNumberExtension/IterableDoubleExtension/IterableIntegerExtension

extension IterableNumberExtension on Iterable<num>
extension IterableDoubleExtension on Iterable<double>
extension IterableIntegerExtension on Iterable<int>

IterableComparableExtension 针对 num 类型的特殊扩展,num 本身也是实现了 Comparable,所以原来扩展的方法也都能用。

IterableDoubleExtensionIterableIntegerExtension 相当于 IterableComparableExtension 的特殊情况,就是这些扩展属性的类型不一样,average 都是 double 类型,别的属性分别是 num、double、int 类型。

IterableIterableExtension

extension IterableIterableExtension<T> on Iterable<Iterable<T>>

Iterable<Iterable<int>> iterable = {[1,3,5], {2,4,6}};  
print(iterable.flattened); // (1, 3, 5, 2, 4, 6)

IterableNullableExtension

extension IterableNullableExtension<T extends Object> on Iterable<T?>

print({1,2,null,5,6}.whereNotNull()); // (1, 2, 5, 6)

ListExtensions

extension ListExtensions<E> on List<E>

和 IterableExtension 的同名方法,只是实现有所不同:

二分查找:

List<int> list = [1,3,6,7];  
// 第二个参数指定的关系是从小到大
list.binarySearch(3, (a,b)=>a-b); // 1

list.binarySearchByCompare(6, (e)=>10-e, (a,b)=>b-a); // 2
list.binarySearchByCompare(6, (e)=>10-e, (a,b)=>b-a, 0, 2); // -1

list.lowerBound(4, (a,b)=>a-b); // 2

排序:

List<int> list = [1,3,2,9,5,7,0];  
list.sortRange(1, 5, (a,b)=>a-b); // [1, 2, 3, 5, 9, 7, 0]
list.shuffleRange(1, 5);
list.reverseRange(1, 5); // [1, 5, 9, 2, 3, 7, 0],3,2,9,5 倒转顺序
list.slice(1,5); // [3, 2, 9, 5]

ListComparableExtensions

extension ListComparableExtensions<E extends Comparable<E>> on List<E>

是 ListExtensions 的特殊版本,用于元素实现了 Comparable 的 List。

binarySearch()lowerBound()sortRange() 三个特殊方法,和 ListExtensions 的同名方法的区别就是因为元素是 Comparable 的,不需要一定要传一个比较器,可以使用默认的。