| OLD | NEW |
| (Empty) |
| 1 part of angular.formatter_internal; | |
| 2 | |
| 3 typedef dynamic _Mapper(dynamic e); | |
| 4 | |
| 5 /** | |
| 6 * Orders the provided [Iterable] by the `expression` predicate. | |
| 7 * | |
| 8 * Example 1: Simple array and single/empty expression. | |
| 9 * | |
| 10 * Assume that you have an array on scope called `colors` and that it has a list | |
| 11 * of these strings – `['red', 'blue', 'green']`. You might sort these in | |
| 12 * ascending order this way: | |
| 13 * | |
| 14 * Colors: <ul> | |
| 15 * <li ng-repeat="color in colors | orderBy:''">{{color}}</li> | |
| 16 * </ul> | |
| 17 * | |
| 18 * That would result in: | |
| 19 * | |
| 20 * <ul> | |
| 21 * <li>blue</li> | |
| 22 * <li>green</li> | |
| 23 * <li>red</li> | |
| 24 * <ul> | |
| 25 * | |
| 26 * The empty string expression, `''`, here signifies sorting in ascending order | |
| 27 * using the default comparator. Using `'+'` would also work as the `+` prefix | |
| 28 * is implied. | |
| 29 * | |
| 30 * To sort in descending order, you would use the `'-'` prefix. | |
| 31 * | |
| 32 * Colors: <ul> | |
| 33 * <li ng-repeat="color in colors | orderBy:'-'">{{color}}</li> | |
| 34 * </ul> | |
| 35 * | |
| 36 * For this simple example, you could have also provided `true` as the addition | |
| 37 * optional parameter which requests a reverse order sort to get the same | |
| 38 * result. | |
| 39 * | |
| 40 * <!-- Same result (descending order) as previous snippet. --> | |
| 41 * Colors: <ul> | |
| 42 * <li ng-repeat="color in colors | orderBy:'':true">{{color}}</li> | |
| 43 * </ul> | |
| 44 * | |
| 45 * Example 2: Complex objects, single expression. | |
| 46 * | |
| 47 * You may provide a more complex expression to sort non-primitives values or | |
| 48 * if you want to sort on a decorated/transformed value. | |
| 49 * | |
| 50 * e.g. Support you have a list `users` that looks like this: | |
| 51 * | |
| 52 * authors = [ | |
| 53 * {firstName: 'Emily', lastName: 'Bronte'}, | |
| 54 * {firstName: 'Mark', lastName: 'Twain'}, | |
| 55 * {firstName: 'Jeffrey', lastName: 'Archer'}, | |
| 56 * {firstName: 'Isaac', lastName: 'Asimov'}, | |
| 57 * {firstName: 'Oscar', lastName: 'Wilde'}, | |
| 58 * ]; | |
| 59 * | |
| 60 * If you want to list the authors sorted by `lastName`, you would use | |
| 61 * | |
| 62 * <li ng-repeat="author in authors | orderBy:'lastName'"> | |
| 63 * {{author.lastName}}, {{author.firstName | |
| 64 * </li> | |
| 65 * | |
| 66 * The string expression, `'lastName'`, indicates that the sort should be on the | |
| 67 * `lastName` property of each item. | |
| 68 * | |
| 69 * Using the lesson from the previous example, you may sort in reverse order of | |
| 70 * lastName using either of the two methods. | |
| 71 * | |
| 72 * <!-- reverse order of last names --> | |
| 73 * <li ng-repeat="author in authors | orderBy:'-lastName'"> | |
| 74 * <!-- also does the same thing --> | |
| 75 * <li ng-repeat="author in authors | orderBy:'lastName':true"> | |
| 76 * | |
| 77 * Note that, while we only discussed string expressions, such as `"lastName"` | |
| 78 * or the empty string, you can also directly provide a custom callable that | |
| 79 * will be called to transform the element before a sort. | |
| 80 * | |
| 81 * <li ng-repeat="author in authors | orderBy:getAuthorId"> | |
| 82 * | |
| 83 * In the previous snippet, `getAuthorId` would evaluate to a callable when | |
| 84 * evaluated on the [Scope] of the `<li>` element. That callable is called once | |
| 85 * for each element in the list (i.e. each author object) and the sort order is | |
| 86 * determined by the sort order of the value mapped by the callable. | |
| 87 * | |
| 88 * Example 3: List expressions | |
| 89 * | |
| 90 * Both a string expression and the callable expression are simple versions of | |
| 91 * the more general list expression. You may pass a list as the orderBy | |
| 92 * expression and this list may consist of either of the string or callable | |
| 93 * expressions you saw in the previous examples. A list expression indicates | |
| 94 * a list of fallback expressions to use when a comparision results in the items | |
| 95 * being equal. | |
| 96 * | |
| 97 * For example, one might want to sort the authors list, first by last name and | |
| 98 * then by first name when the last names are equal. You would do that like | |
| 99 * this: | |
| 100 * | |
| 101 * <li ng-repeat="author in authors | orderBy:['lastName', 'firstName']"> | |
| 102 * | |
| 103 * The items in such a list may either be string expressions or callables. The | |
| 104 * list itself might be provided as an expression that is looked up on the scope | |
| 105 * chain. | |
| 106 */ | |
| 107 @Formatter(name: 'orderBy') | |
| 108 class OrderBy implements Function { | |
| 109 Parser _parser; | |
| 110 | |
| 111 OrderBy(this._parser); | |
| 112 | |
| 113 static _nop(e) => e; | |
| 114 static bool _isNonZero(int n) => (n != 0); | |
| 115 static int _returnZero() => 0; | |
| 116 static int _defaultComparator(a, b) => Comparable.compare(a, b); | |
| 117 static int _reverseComparator(a, b) => _defaultComparator(b, a); | |
| 118 | |
| 119 static int _compareLists(List a, List b, List<Comparator> comparators) { | |
| 120 return new Iterable.generate(a.length, (i) => comparators[i](a[i], b[i])) | |
| 121 .firstWhere(_isNonZero, orElse: _returnZero); | |
| 122 } | |
| 123 | |
| 124 static List _sorted( | |
| 125 List items, List<_Mapper> mappers, List<Comparator> comparators, bool desc
ending) { | |
| 126 // Do the standard decorate-sort-undecorate aka Schwartzian dance since Dart | |
| 127 // doesn't support a key/transform parameter to sort(). | |
| 128 // Ref: http://en.wikipedia.org/wiki/Schwartzian_transform | |
| 129 mapper(e) => mappers.map((m) => m(e)).toList(growable: false); | |
| 130 List decorated = items.map(mapper).toList(growable: false); | |
| 131 List<int> indices = new Iterable.generate(decorated.length, _nop).toList(gro
wable: false); | |
| 132 comparator(i, j) => _compareLists(decorated[i], decorated[j], comparators); | |
| 133 indices.sort((descending) ? (i, j) => comparator(j, i) : comparator); | |
| 134 return indices.map((i) => items[i]).toList(growable: false); | |
| 135 } | |
| 136 | |
| 137 /** | |
| 138 * expression: String/Function or Array of String/Function. | |
| 139 */ | |
| 140 List call(List items, var expression, [bool descending=false]) { | |
| 141 if (items == null) { | |
| 142 return null; | |
| 143 } | |
| 144 List expressions = null; | |
| 145 if (expression is String || expression is _Mapper) { | |
| 146 expressions = [expression]; | |
| 147 } else if (expression is List) { | |
| 148 expressions = expression as List; | |
| 149 } | |
| 150 if (expressions == null || expressions.length == 0) { | |
| 151 // AngularJS behavior. You must have an expression to get any work done. | |
| 152 return items; | |
| 153 } | |
| 154 int numExpressions = expressions.length; | |
| 155 List<_Mapper> mappers = new List(numExpressions); | |
| 156 List<Comparator> comparators = new List<Comparator>(numExpressions); | |
| 157 for (int i = 0; i < numExpressions; i++) { | |
| 158 expression = expressions[i]; | |
| 159 if (expression is String) { | |
| 160 var strExp = expression as String; | |
| 161 var desc = false; | |
| 162 if (strExp.startsWith('-') || strExp.startsWith('+')) { | |
| 163 desc = strExp.startsWith('-'); | |
| 164 strExp = strExp.substring(1); | |
| 165 } | |
| 166 comparators[i] = desc ? _reverseComparator : _defaultComparator; | |
| 167 if (strExp == '') { | |
| 168 mappers[i] = _nop; | |
| 169 } else { | |
| 170 Expression parsed = _parser(strExp); | |
| 171 mappers[i] = (e) => parsed.eval(e); | |
| 172 } | |
| 173 } else if (expression is _Mapper) { | |
| 174 mappers[i] = (expression as _Mapper); | |
| 175 comparators[i] = _defaultComparator; | |
| 176 } | |
| 177 } | |
| 178 return _sorted(items, mappers, comparators, descending); | |
| 179 } | |
| 180 } | |
| OLD | NEW |