|
4 | 4 |
|
5 | 5 | import 'dart:math' show Random; |
6 | 6 |
|
| 7 | +import '../collection.dart' show DelegatingIterable; |
7 | 8 | import 'algorithms.dart'; |
8 | 9 | import 'functions.dart' as functions; |
9 | 10 | import 'utils.dart'; |
@@ -56,6 +57,99 @@ extension IterableExtension<T> on Iterable<T> { |
56 | 57 | return chosen; |
57 | 58 | } |
58 | 59 |
|
| 60 | + /// The elements of this iterable separated by [separator]. |
| 61 | + /// |
| 62 | + /// Emits the same elements as this iterable, and also emits |
| 63 | + /// a [separator] between any two of those elements. |
| 64 | + /// |
| 65 | + /// If [before] is set to `true`, a [separator] is also |
| 66 | + /// emitted before the first element. |
| 67 | + /// If [after] is set to `true`, a [separator] is also |
| 68 | + /// emitted after the last element. |
| 69 | + /// |
| 70 | + /// If this iterable is empty, [before] and [after] have no effect. |
| 71 | + /// |
| 72 | + /// Example: |
| 73 | + /// ```dart |
| 74 | + /// print(([1, 2, 3] as Iterable<int>).separated(-1)); // (1, -1, 2, -1, 3) |
| 75 | + /// print(([1] as Iterable<int>).separated(-1)); // (1) |
| 76 | + /// print(([] as Iterable<int>).separated(-1)); // () |
| 77 | + /// |
| 78 | + /// print(([1, 2, 3] as Iterable<int>).separated( |
| 79 | + /// -1, |
| 80 | + /// before: true, |
| 81 | + /// )); // (-1, 1, -1, 2, -1, 3) |
| 82 | + /// |
| 83 | + /// print(([1] as Iterable<int>).separated( |
| 84 | + /// -1, |
| 85 | + /// before: true, |
| 86 | + /// after: true, |
| 87 | + /// )); // (-1, 1, -1) |
| 88 | + /// |
| 89 | + /// print(([] as Iterable<int>).separated( |
| 90 | + /// -1, |
| 91 | + /// before: true, |
| 92 | + /// after: true, |
| 93 | + /// )); // () |
| 94 | + /// ``` |
| 95 | + Iterable<T> separated(T separator, |
| 96 | + {bool before = false, bool after = false}) => |
| 97 | + _SeparatedIterable<T>(this, separator, before, after); |
| 98 | + |
| 99 | + /// Creates list with the elements of this iterable separated by [separator]. |
| 100 | + /// |
| 101 | + /// Returns a new list which contains the same elements as this iterable, |
| 102 | + /// with a [separator] between any two of those elements. |
| 103 | + /// |
| 104 | + /// If [before] is set to `true`, a [separator] is also |
| 105 | + /// added before the first element. |
| 106 | + /// If [after] is set to `true`, a [separator] is also |
| 107 | + /// added after the last element. |
| 108 | + /// |
| 109 | + /// If this iterable is empty, [before] and [after] have no effect. |
| 110 | + /// |
| 111 | + /// Example: |
| 112 | + /// ```dart |
| 113 | + /// print([1, 2, 3].separatedList(-1)); // [1, -1, 2, -1, 3] |
| 114 | + /// print([1].separatedList(-1)); // [1] |
| 115 | + /// print([].separatedList(-1)); // [] |
| 116 | + /// |
| 117 | + /// print([1, 2, 3].separatedList( |
| 118 | + /// -1, |
| 119 | + /// before: true, |
| 120 | + /// )); // [-1, 1, -1, 2, -1, 3] |
| 121 | + /// |
| 122 | + /// print([1].separatedList( |
| 123 | + /// -1, |
| 124 | + /// before: true, |
| 125 | + /// after: true, |
| 126 | + /// )); // [-1, 1, -1] |
| 127 | + /// |
| 128 | + /// print([].separatedList( |
| 129 | + /// -1, |
| 130 | + /// before: true, |
| 131 | + /// after: true, |
| 132 | + /// )); // [] |
| 133 | + /// ``` |
| 134 | + List<T> separatedList(T separator, |
| 135 | + {bool before = false, bool after = false}) { |
| 136 | + var result = <T>[]; |
| 137 | + var iterator = this.iterator; |
| 138 | + if (iterator.moveNext()) { |
| 139 | + if (before) result.add(separator); |
| 140 | + while (true) { |
| 141 | + result.add(iterator.current); |
| 142 | + if (iterator.moveNext()) { |
| 143 | + result.add(separator); |
| 144 | + } else { |
| 145 | + break; |
| 146 | + } |
| 147 | + } |
| 148 | + if (after) result.add(separator); |
| 149 | + } |
| 150 | + return result; |
| 151 | + } |
| 152 | + |
59 | 153 | /// The elements that do not satisfy [test]. |
60 | 154 | Iterable<T> whereNot(bool Function(T element) test) => |
61 | 155 | where((element) => !test(element)); |
@@ -1056,3 +1150,206 @@ extension ComparatorExtension<T> on Comparator<T> { |
1056 | 1150 | return result; |
1057 | 1151 | }; |
1058 | 1152 | } |
| 1153 | + |
| 1154 | +/// Implementation of [IterableExtension.separated]. |
| 1155 | +/// |
| 1156 | +/// Optimizes direct accesses. |
| 1157 | +class _SeparatedIterable<T> extends Iterable<T> { |
| 1158 | + final T _separator; |
| 1159 | + final Iterable<T> _elements; |
| 1160 | + |
| 1161 | + static const int _afterFlag = 1 << 0; |
| 1162 | + static const int _beforeFlag = 1 << 1; |
| 1163 | + |
| 1164 | + /// Two bit-flags, for whether the `before` and `after` arguments were `true`. |
| 1165 | + final int _flags; |
| 1166 | + |
| 1167 | + _SeparatedIterable(this._elements, this._separator, bool before, bool after) |
| 1168 | + : _flags = (before ? _beforeFlag : 0) + (after ? _afterFlag : 0); |
| 1169 | + |
| 1170 | + @override |
| 1171 | + bool get isEmpty => _elements.isEmpty; |
| 1172 | + @override |
| 1173 | + bool get isNotEmpty => _elements.isNotEmpty; |
| 1174 | + @override |
| 1175 | + int get length { |
| 1176 | + var length = _elements.length; |
| 1177 | + if (length != 0) { |
| 1178 | + length = length * 2 - 1 + (_flags & 1) + (_flags >> 1); |
| 1179 | + } |
| 1180 | + return length; |
| 1181 | + } |
| 1182 | + |
| 1183 | + @override |
| 1184 | + T elementAt(int index) { |
| 1185 | + RangeError.checkNotNegative(index, 'index'); |
| 1186 | + // Figure out which element must exist in [_elements] for this index |
| 1187 | + // to exist in the separated output. |
| 1188 | + var indexWithoutBefore = index - (_flags >> 1); |
| 1189 | + var elementIndex = indexWithoutBefore ~/ 2; // Rounds both -1 and 1 to 0. |
| 1190 | + if (indexWithoutBefore.isEven) { |
| 1191 | + // It's an element. |
| 1192 | + return _elements.elementAt(elementIndex); |
| 1193 | + } |
| 1194 | + // It's a separator after that element (or before the first element). |
| 1195 | + // Check if that element exists, unless the `_afterFlag` is set, |
| 1196 | + // in which case to check if the next element exists by adding 1 |
| 1197 | + // to elementIndex. |
| 1198 | + // (But if `index` is zero, it's the before separator, so it should |
| 1199 | + // check that a first element exists.) |
| 1200 | + if (index != 0) { |
| 1201 | + assert(_afterFlag == 1); |
| 1202 | + elementIndex += (_flags ^ _afterFlag) & _afterFlag; |
| 1203 | + } |
| 1204 | + _elements.elementAt(elementIndex); // If throws, not an element. |
| 1205 | + return _separator; |
| 1206 | + } |
| 1207 | + |
| 1208 | + @override |
| 1209 | + T get first { |
| 1210 | + if (_flags & _beforeFlag == 0) return _elements.first; |
| 1211 | + if (_elements.isNotEmpty) return _separator; |
| 1212 | + throw StateError('No element'); |
| 1213 | + } |
| 1214 | + |
| 1215 | + @override |
| 1216 | + T get last { |
| 1217 | + if (_flags & _afterFlag == 0) return _elements.last; |
| 1218 | + if (_elements.isNotEmpty) return _separator; |
| 1219 | + throw StateError('No element'); |
| 1220 | + } |
| 1221 | + |
| 1222 | + @override |
| 1223 | + Iterable<T> take(int count) { |
| 1224 | + if (count == 0) return Iterable<T>.empty(); |
| 1225 | + var beforeCount = _flags >> 1; |
| 1226 | + if (count == 1) { |
| 1227 | + if (beforeCount == 0) { |
| 1228 | + return _elements.take(1); |
| 1229 | + } |
| 1230 | + // return Iterable<T>.value(_separator); // Why you no exist?! |
| 1231 | + return DelegatingIterable<T>([_separator]); |
| 1232 | + } |
| 1233 | + var countWithoutBefore = count - beforeCount; |
| 1234 | + var elementCount = (countWithoutBefore + 1) >> 1; |
| 1235 | + return _SeparatedIterable<T>( |
| 1236 | + _elements.take(elementCount), |
| 1237 | + _separator, |
| 1238 | + beforeCount != 0, |
| 1239 | + countWithoutBefore.isEven, |
| 1240 | + ); |
| 1241 | + } |
| 1242 | + |
| 1243 | + @override |
| 1244 | + Iterable<T> skip(int count) { |
| 1245 | + if (count == 0) return this; |
| 1246 | + var beforeCount = _flags >> 1; |
| 1247 | + var countWithoutBefore = count - beforeCount; |
| 1248 | + var hasAfter = _flags & _afterFlag != 0; |
| 1249 | + if (countWithoutBefore.isOdd && hasAfter) { |
| 1250 | + // Remainder could be just the final separator, which cannot |
| 1251 | + // be created by a `_SeparatedIterable`. |
| 1252 | + // (Unlike `take`, cannot see that without iterating.) |
| 1253 | + return super.skip(count); |
| 1254 | + } |
| 1255 | + // Starts or ends on an element, not a separator, |
| 1256 | + // so remainder cannot be a single separator. |
| 1257 | + var elementCount = (countWithoutBefore + 1) >> 1; |
| 1258 | + return _SeparatedIterable<T>( |
| 1259 | + elementCount == 0 ? _elements : _elements.skip(elementCount), |
| 1260 | + _separator, |
| 1261 | + countWithoutBefore.isOdd, |
| 1262 | + hasAfter, |
| 1263 | + ); |
| 1264 | + } |
| 1265 | + |
| 1266 | + @override |
| 1267 | + Iterator<T> get iterator => |
| 1268 | + _SeparatedIterator<T>(_elements.iterator, _separator, _flags); |
| 1269 | +} |
| 1270 | + |
| 1271 | +/// Iterator for [_SeparatedIterable]. |
| 1272 | +class _SeparatedIterator<T> implements Iterator<T> { |
| 1273 | + final T _separator; |
| 1274 | + final Iterator<T> _elements; |
| 1275 | + |
| 1276 | + // Flags set in [_state]. |
| 1277 | + |
| 1278 | + /// Set if adding a separator after the last element. |
| 1279 | + /// |
| 1280 | + /// State never changes, just storing a boolean as a bit. |
| 1281 | + static const _noAddAfterFlag = 1 << 0; |
| 1282 | + |
| 1283 | + // Set when the next element to emit is a separator. |
| 1284 | + // |
| 1285 | + // Otherwise the element to emit is [_elements.current]. |
| 1286 | + static const _separatorFlag = 1 << 1; |
| 1287 | + |
| 1288 | + // Set when next step should check if there is a next element. |
| 1289 | + // |
| 1290 | + // If there is no next element, iteration ends. |
| 1291 | + static const _ifHasNextFlag = 1 << 2; |
| 1292 | + |
| 1293 | + /// Current state. |
| 1294 | + /// |
| 1295 | + /// A combination of the [_noAddAfterFlag], [_separatorFlag] |
| 1296 | + /// and [_ifHasNextFlag]. |
| 1297 | + /// |
| 1298 | + /// Transitions: |
| 1299 | + /// * If `_ifHasNextFlag`: |
| 1300 | + /// - if `!_elements.moveNext()`, then end. |
| 1301 | + /// (No state change, next call will do the same). |
| 1302 | + /// - otherwise continue. |
| 1303 | + /// * If `_separatorFlag`: |
| 1304 | + /// - emit `_separator`, |
| 1305 | + /// - clear `_separatorFlag` (next is an element), |
| 1306 | + /// - toggle `_ifHasNextFlag`. |
| 1307 | + /// * else: |
| 1308 | + /// - emit `_elements.current`, |
| 1309 | + /// - set `_separatorFlag` (next will be a separator), |
| 1310 | + /// - set `_ifHasNextFlag` if `_noAddAfterFlag` is set. |
| 1311 | + /// |
| 1312 | + /// Starts with `ifHasNextFlag` set, |
| 1313 | + /// with `_separatorFlag` set if the `before` parameter of the iterable |
| 1314 | + /// was `true`, and with `noAddAfterFlag` set if the `after` parameter |
| 1315 | + /// of the iterable was `false`. |
| 1316 | + int _state; |
| 1317 | + |
| 1318 | + T? _current; |
| 1319 | + |
| 1320 | + _SeparatedIterator(this._elements, this._separator, int flags) |
| 1321 | + : assert(_noAddAfterFlag == _SeparatedIterable._afterFlag), |
| 1322 | + assert(_separatorFlag == _SeparatedIterable._beforeFlag), |
| 1323 | + // `_separatorFlag` set if `_beforeFlag` was set. |
| 1324 | + // `_noAddAfterFlag` set if `_afterFlag` was not. |
| 1325 | + // `_ifHasNextFlag` always set at the start. |
| 1326 | + _state = (flags ^ _noAddAfterFlag) | _ifHasNextFlag; |
| 1327 | + |
| 1328 | + @override |
| 1329 | + T get current => _current as T; |
| 1330 | + |
| 1331 | + @override |
| 1332 | + bool moveNext() { |
| 1333 | + var state = _state; |
| 1334 | + if (state & _ifHasNextFlag == 0 || _elements.moveNext()) { |
| 1335 | + if (state & _separatorFlag != 0) { |
| 1336 | + _current = _separator; |
| 1337 | + // Next is not separator. |
| 1338 | + // Check if there is a next if this call didn't. |
| 1339 | + state ^= _separatorFlag | _ifHasNextFlag; |
| 1340 | + } else { |
| 1341 | + _current = _elements.current; |
| 1342 | + // Next is separator. |
| 1343 | + // Check if there is a next if not adding separator after last element. |
| 1344 | + state = (state & _noAddAfterFlag) * (_noAddAfterFlag | _ifHasNextFlag) + |
| 1345 | + _separatorFlag; |
| 1346 | + } |
| 1347 | + _state = state; |
| 1348 | + return true; |
| 1349 | + } |
| 1350 | + // Next call will check `_elements.moveNext()` again. |
| 1351 | + assert(state & _ifHasNextFlag != 0); |
| 1352 | + _current = null; |
| 1353 | + return false; |
| 1354 | + } |
| 1355 | +} |
0 commit comments