// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. // @dart = 2.9 part of utilslib; typedef num NumericValueSelector(T value); /** * General purpose collection utilities. * TODO(jmesserly): make these top level functions? */ class CollectionUtils { static void insertAt(List arr, int pos, value) { assert(pos >= 0); assert(pos <= arr.length); if (pos == arr.length) { arr.add(value); } else { // TODO(sigmund): replace this push with a call that ensures capacity // (currently not supported in the JS implementation of list). E.g. // [: arr.length = arr.length + 1; :] arr.add(null); // shift elements from [pos] (note: arr already has null @ length - 1) for (int i = arr.length - 2; i >= pos; i--) { arr[i + 1] = arr[i]; } arr[pos] = value; // TODO(jmesserly): we won't need to do this once List // implements insertAt if (arr is ObservableList) { // TODO(jmesserly): shouldn't need to cast after testing instanceof ObservableList obs = arr; obs.recordListInsert(pos, value); } } } /** * Finds the item in [source] that matches [test]. Returns null if * no item matches. The typing should be: * T find(Iterable source, bool test(T item)), but we don't have generic * functions. */ static find(Iterable source, bool test(item)) { for (final item in source) { if (test(item)) return item; } return null; } /** Compute the minimum of an iterable. Returns null if empty. */ static num min(Iterable source) { final iter = source.iterator; if (!iter.moveNext()) { return null; } num best = iter.current; while (iter.moveNext()) { best = Math.min(best, iter.current); } return best; } /** Compute the maximum of an iterable. Returns null if empty. */ static num max(Iterable source) { final iter = source.iterator; if (!iter.moveNext()) { return null; } num best = iter.current; while (iter.moveNext()) { best = Math.max(best, iter.current); } return best; } /** Orders an iterable by its values, or by a key selector. */ static List orderBy(Iterable source, [NumericValueSelector selector = null]) { final result = new List.from(source); sortBy(result, selector); return result; } /** Sorts a list by its values, or by a key selector. */ // TODO(jmesserly): we probably don't want to call the key selector more than // once for a given element. This would improve performance and the API // contract could be stronger. static void sortBy(List list, [NumericValueSelector selector = null]) { if (selector != null) { list.sort((x, y) => selector(x) - selector(y)); } else { list.sort((x, y) => x - y); } } /** Compute the sum of an iterable. An empty iterable is an error. */ static num sum(Iterable source, [NumericValueSelector selector = null]) { final iter = source.iterator; bool wasEmpty = true; num total = 0; if (selector != null) { for (var element in source) { wasEmpty = false; total += selector(element); } } else { for (num element in source) { wasEmpty = false; total += element; } } if (wasEmpty) throw new StateError("No elements"); return total; } // TODO(jmesserly): something like should exist on Map, either a method or a // constructor, see bug #5340679 static void copyMap(Map dest, Map source) { for (final k in source.keys) { dest[k] = source[k]; } } }