Skip to content

Commit

Permalink
Add sorting strategy (#9)
Browse files Browse the repository at this point in the history
* Add sorting strategy

* Update mocks

* Add merge sort

* Add strategy tests

* Bump version

* Update dartdoc

* Use relative imports

* Use mergeSort from package:collection

* Mention new sorting strategies in changelog

* Split tests

* Add missing dot in dartdoc

Co-authored-by: Albert Wolszon <[email protected]>

Co-authored-by: Albert Wolszon <[email protected]>
  • Loading branch information
shilangyu and Albert221 authored Jun 29, 2022
1 parent ed38473 commit ad1474e
Show file tree
Hide file tree
Showing 10 changed files with 135 additions and 13 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# 2.1.0

- Add `SortingStrategy` which allows for customizing the sorting algorithm.
- Add `DefaultSortingStrategy`, which uses Dart's default (unstable) sorting method.
- Add `MergeSortingStrategy`, which uses merge sort, a stable sorting algorithm.

# 2.0.0+1

- Replace image with snippet in README.
Expand Down
3 changes: 3 additions & 0 deletions lib/sorted.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ export 'src/rules/comparable.dart';
export 'src/rules/comparator.dart';
export 'src/rules/group_ordered.dart';
export 'src/rules/ordered.dart';
export 'src/sorting_strategies/default.dart';
export 'src/sorting_strategies/merge.dart';
export 'src/sorting_strategy.dart';
37 changes: 26 additions & 11 deletions lib/src/extension.dart
Original file line number Diff line number Diff line change
@@ -1,21 +1,36 @@
import 'rule.dart';
import 'sorting_strategies/default.dart';
import 'sorting_strategies/merge.dart';
import 'sorting_strategy.dart';

extension Sorted<T> on Iterable<T> {
/// Returns a sorted list according to the `rules`. Order matters, so if
/// first rule considers two items equal, next one will decide and so on.
List<T> sorted(List<SortedRule> rules) {
if (rules.isEmpty) return List.of(this);
///
/// A sorting strategy can be provided through `sortingStrategy` which
/// will perform the final sorting procedure. Defaults to [DefaultSortingStrategy].
/// Consider using [MergeSortingStrategy] if a stable sort is needed.
List<T> sorted(
List<SortedRule> rules, {
SortingStrategy<T>? sortingStrategy,
}) {
sortingStrategy ??= DefaultSortingStrategy<T>();

return List.of(this)
..sort((a, b) {
int result = 0;
for (final rule in rules) {
result = rule.compareComplex(a, b);
final list = List.of(this);

if (result != 0) return result;
}
if (rules.isEmpty) return list;

return result;
});
sortingStrategy.sort(list, (a, b) {
int result = 0;
for (final rule in rules) {
result = rule.compareComplex(a, b);

if (result != 0) return result;
}

return result;
});

return list;
}
}
11 changes: 11 additions & 0 deletions lib/src/sorting_strategies/default.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import '../sorting_strategy.dart';

/// A [SortingStrategy] which uses Dart's default (unstable) sorting method.
class DefaultSortingStrategy<T> implements SortingStrategy<T> {
const DefaultSortingStrategy();

@override
void sort(List<T> list, Comparator<T> comparator) {
list.sort(comparator);
}
}
13 changes: 13 additions & 0 deletions lib/src/sorting_strategies/merge.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import 'package:collection/collection.dart';

import '../sorting_strategy.dart';

/// A [SortingStrategy] which uses merge sort, a stable sorting algorithm.
class MergeSortingStrategy<T> implements SortingStrategy<T> {
const MergeSortingStrategy();

@override
void sort(List<T> list, Comparator<T> comparator) {
mergeSort(list, compare: comparator);
}
}
13 changes: 13 additions & 0 deletions lib/src/sorting_strategy.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import 'sorting_strategies/default.dart';
import 'sorting_strategies/merge.dart';

/// Strategy performing the final sorting procedure given a list and a single comparator.
///
/// See also:
///
/// * [DefaultSortingStrategy], which uses Dart's default (unstable) sorting method.
/// * [MergeSortingStrategy], which uses merge sort, a stable sorting algorithm.
abstract class SortingStrategy<T> {
/// Sorts [list] in place given a [comparator].
void sort(List<T> list, Comparator<T> comparator);
}
5 changes: 4 additions & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ name: sorted
description: >
Sort lists of complex objects with ease.
Simple but powerful extension method.
version: 2.0.0+1
version: 2.1.0
homepage: https://github.com/Albert221/sorted

environment:
sdk: ">=2.12.0 <3.0.0"

dependencies:
collection: ^1.15.0

dev_dependencies:
build_runner: ^2.1.1
lint: ^1.5.3
Expand Down
14 changes: 14 additions & 0 deletions test/default_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import 'package:sorted/sorted.dart';
import 'package:test/test.dart';

void main() {
group('DefaultSortingStrategy', () {
test('sorts the list in-place', () {
final list = [1, 3, 2, 1];

const DefaultSortingStrategy<int>().sort(list, (a, b) => a - b);

expect(list, [1, 1, 2, 3]);
});
});
}
9 changes: 8 additions & 1 deletion test/extension_test.mocks.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
// Mocks generated by Mockito 5.0.0 from annotations
// Mocks generated by Mockito 5.2.0 from annotations
// in sorted/test/extension_test.dart.
// Do not manually edit this file.

import 'package:mockito/mockito.dart' as _i1;
import 'package:sorted/src/rule.dart' as _i2;

// ignore_for_file: type=lint
// ignore_for_file: avoid_redundant_argument_values
// ignore_for_file: avoid_setters_without_getters
// ignore_for_file: comment_references
// ignore_for_file: implementation_imports
// ignore_for_file: invalid_use_of_visible_for_testing_member
// ignore_for_file: prefer_const_constructors
// ignore_for_file: unnecessary_parenthesis
// ignore_for_file: camel_case_types

/// A class which mocks [SortedRule].
///
Expand Down
37 changes: 37 additions & 0 deletions test/merge_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import 'package:sorted/sorted.dart';
import 'package:test/test.dart';

void main() {
group('MergeSortingStrategy', () {
test('sorts the list in-place', () {
final list = [1, 3, 2, 1];

const MergeSortingStrategy<int>().sort(list, (a, b) => a - b);

expect(list, [1, 1, 2, 3]);
});

test('sort is stable', () {
final list = [
{'prop1': 1, 'prop2': 0},
{'prop1': 3, 'prop2': 0},
{'prop1': 1, 'prop2': 1},
{'prop1': 1, 'prop2': 2},
{'prop1': 2, 'prop2': 0},
{'prop1': 1, 'prop2': 1},
];

const MergeSortingStrategy<Map<String, int>>()
.sort(list, (a, b) => a['prop1']! - b['prop1']!);

expect(list, [
{'prop1': 1, 'prop2': 0},
{'prop1': 1, 'prop2': 1},
{'prop1': 1, 'prop2': 2},
{'prop1': 1, 'prop2': 1},
{'prop1': 2, 'prop2': 0},
{'prop1': 3, 'prop2': 0},
]);
});
});
}

0 comments on commit ad1474e

Please sign in to comment.