OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 import 'dart:math' as math; |
| 6 import 'dart:collection'; |
| 7 |
| 8 import 'utils.dart'; |
| 9 |
| 10 // TODO(nweiz): When sdk#26488 is fixed, use overloads to ensure that if [key] |
| 11 // or [value] isn't passed, `K2`/`V2` defaults to `K1`/`V1`, respectively. |
| 12 /// Creates a new map from [map] with new keys and values. |
| 13 /// |
| 14 /// The return values of [key] are used as the keys and the return values of |
| 15 /// [value] are used as the values for the new map. |
| 16 Map<K2, V2> mapMap<K1, V1, K2, V2>(Map<K1, V1> map, |
| 17 {K2 key(K1 key, V1 value), V2 value(K1 key, V1 value)}) { |
| 18 key ??= (mapKey, _) => mapKey as K2; |
| 19 value ??= (_, mapValue) => mapValue as V2; |
| 20 |
| 21 var result = <K2, V2>{}; |
| 22 map.forEach((mapKey, mapValue) { |
| 23 result[key(mapKey, mapValue)] = value(mapKey, mapValue); |
| 24 }); |
| 25 return result; |
| 26 } |
| 27 |
| 28 /// Returns a new map with all key/value pairs in both [map1] and [map2]. |
| 29 /// |
| 30 /// If there are keys that occur in both maps, the [value] function is used to |
| 31 /// select the value that goes into the resulting map based on the two original |
| 32 /// values. If [value] is omitted, the value from [map2] is used. |
| 33 Map<K, V> mergeMaps<K, V>(Map<K, V> map1, Map<K, V> map2, |
| 34 {V value(V value1, V value2)}) { |
| 35 var result = new Map<K, V>.from(map1); |
| 36 if (value == null) return result..addAll(map2); |
| 37 |
| 38 map2.forEach((key, mapValue) { |
| 39 result[key] = |
| 40 result.containsKey(key) ? value(result[key], mapValue) : mapValue; |
| 41 }); |
| 42 return result; |
| 43 } |
| 44 |
| 45 /// Groups the elements in [values] by the value returned by [key]. |
| 46 /// |
| 47 /// Returns a map from keys computed by [key] to a list of all values for which |
| 48 /// [key] returns that key. The values appear in the list in the same relative |
| 49 /// order as in [values]. |
| 50 Map<T, List<S>> groupBy<S, T>(Iterable<S> values, T key(S element)) { |
| 51 var map = <T, List<S>>{}; |
| 52 for (var element in values) { |
| 53 var list = map.putIfAbsent(key(element), () => []); |
| 54 list.add(element); |
| 55 } |
| 56 return map; |
| 57 } |
| 58 |
| 59 /// Returns the element of [values] for which [orderBy] returns the minimum |
| 60 /// value. |
| 61 /// |
| 62 /// The values returned by [orderBy] are compared using the [compare] function. |
| 63 /// If [compare] is omitted, values must implement [Comparable<T>] and they are |
| 64 /// compared using their [Comparable.compareTo]. |
| 65 S minBy<S, T>(Iterable<S> values, T orderBy(S element), |
| 66 {int compare(T value1, T value2)}) { |
| 67 compare ??= defaultCompare<T>(); |
| 68 |
| 69 S minValue; |
| 70 T minOrderBy; |
| 71 for (var element in values) { |
| 72 var elementOrderBy = orderBy(element); |
| 73 if (minOrderBy == null || compare(elementOrderBy, minOrderBy) < 0) { |
| 74 minValue = element; |
| 75 minOrderBy = elementOrderBy; |
| 76 } |
| 77 } |
| 78 return minValue; |
| 79 } |
| 80 |
| 81 /// Returns the element of [values] for which [orderBy] returns the maximum |
| 82 /// value. |
| 83 /// |
| 84 /// The values returned by [orderBy] are compared using the [compare] function. |
| 85 /// If [compare] is omitted, values must implement [Comparable<T>] and they are |
| 86 /// compared using their [Comparable.compareTo]. |
| 87 S maxBy<S, T>(Iterable<S> values, T orderBy(S element), |
| 88 {int compare(T value1, T value2)}) { |
| 89 compare ??= defaultCompare<T>(); |
| 90 |
| 91 S maxValue; |
| 92 T maxOrderBy; |
| 93 for (var element in values) { |
| 94 var elementOrderBy = orderBy(element); |
| 95 if (maxOrderBy == null || compare(elementOrderBy, maxOrderBy) > 0) { |
| 96 maxValue = element; |
| 97 maxOrderBy = elementOrderBy; |
| 98 } |
| 99 } |
| 100 return maxValue; |
| 101 } |
| 102 |
| 103 /// Returns the [transitive closure][] of [graph]. |
| 104 /// |
| 105 /// [transitive closure]: https://en.wikipedia.org/wiki/Transitive_closure |
| 106 /// |
| 107 /// Interprets [graph] as a directed graph with a vertex for each key and edges |
| 108 /// from each key to the values that the key maps to. |
| 109 /// |
| 110 /// Assumes that every vertex in the graph has a key to represent it, even if |
| 111 /// that vertex has no outgoing edges. This isn't checked, but if it's not |
| 112 /// satisfied, the function may crash or provide unexpected output. For example, |
| 113 /// `{"a": ["b"]}` is not valid, but `{"a": ["b"], "b": []}` is. |
| 114 Map<T, Set<T>> transitiveClosure<T>(Map<T, Iterable<T>> graph) { |
| 115 // This uses [Warshall's algorithm][], modified not to add a vertex from each |
| 116 // node to itself. |
| 117 // |
| 118 // [Warshall's algorithm]: https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshal
l_algorithm#Applications_and_generalizations. |
| 119 var result = <T, Set<T>>{}; |
| 120 graph.forEach((vertex, edges) { |
| 121 result[vertex] = new Set<T>.from(edges); |
| 122 }); |
| 123 |
| 124 // Lists are faster to iterate than maps, so we create a list since we're |
| 125 // iterating repeatedly. |
| 126 var keys = graph.keys.toList(); |
| 127 for (var vertex1 in keys) { |
| 128 for (var vertex2 in keys) { |
| 129 for (var vertex3 in keys) { |
| 130 if (result[vertex2].contains(vertex1) && |
| 131 result[vertex1].contains(vertex3)) { |
| 132 result[vertex2].add(vertex3); |
| 133 } |
| 134 } |
| 135 } |
| 136 } |
| 137 |
| 138 return result; |
| 139 } |
| 140 |
| 141 /// Returns the [strongly connected components][] of [graph], in topological |
| 142 /// order. |
| 143 /// |
| 144 /// [strongly connected components]: https://en.wikipedia.org/wiki/Strongly_conn
ected_component |
| 145 /// |
| 146 /// Interprets [graph] as a directed graph with a vertex for each key and edges |
| 147 /// from each key to the values that the key maps to. |
| 148 /// |
| 149 /// Assumes that every vertex in the graph has a key to represent it, even if |
| 150 /// that vertex has no outgoing edges. This isn't checked, but if it's not |
| 151 /// satisfied, the function may crash or provide unexpected output. For example, |
| 152 /// `{"a": ["b"]}` is not valid, but `{"a": ["b"], "b": []}` is. |
| 153 List<Set<T>> stronglyConnectedComponents<T>(Map<T, Iterable<T>> graph) { |
| 154 // This uses [Tarjan's algorithm][]. |
| 155 // |
| 156 // [Tarjan's algorithm]: https://en.wikipedia.org/wiki/Tarjan%27s_strongly_con
nected_components_algorithm |
| 157 var index = 0; |
| 158 var stack = <T>[]; |
| 159 var result = <Set<T>>[]; |
| 160 |
| 161 // The order of these doesn't matter, so we use un-linked implementations to |
| 162 // avoid unnecessary overhead. |
| 163 var indices = new HashMap<T, int>(); |
| 164 var lowLinks = new HashMap<T, int>(); |
| 165 var onStack = new HashSet<T>(); |
| 166 |
| 167 strongConnect(T vertex) { |
| 168 indices[vertex] = index; |
| 169 lowLinks[vertex] = index; |
| 170 index++; |
| 171 |
| 172 stack.add(vertex); |
| 173 onStack.add(vertex); |
| 174 |
| 175 for (var successor in graph[vertex]) { |
| 176 if (!indices.containsKey(successor)) { |
| 177 strongConnect(successor); |
| 178 lowLinks[vertex] = math.min(lowLinks[vertex], lowLinks[successor]); |
| 179 } else if (onStack.contains(successor)) { |
| 180 lowLinks[vertex] = math.min(lowLinks[vertex], lowLinks[successor]); |
| 181 } |
| 182 } |
| 183 |
| 184 if (lowLinks[vertex] == indices[vertex]) { |
| 185 var component = new Set<T>(); |
| 186 T neighbor; |
| 187 do { |
| 188 neighbor = stack.removeLast(); |
| 189 onStack.remove(neighbor); |
| 190 component.add(neighbor); |
| 191 } while (neighbor != vertex); |
| 192 result.add(component); |
| 193 } |
| 194 } |
| 195 |
| 196 for (var vertex in graph.keys) { |
| 197 if (!indices.containsKey(vertex)) strongConnect(vertex); |
| 198 } |
| 199 |
| 200 // Tarjan's algorithm produces a reverse-topological sort, so we reverse it to |
| 201 // get a normal topological sort. |
| 202 return result.reversed.toList(); |
| 203 } |
OLD | NEW |