diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml deleted file mode 100644 index c75b33f..0000000 --- a/.github/workflows/dart.yml +++ /dev/null @@ -1,41 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - -name: Dart - -on: - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - # Note: This workflow uses the latest stable version of the Dart SDK. - # You can specify other versions if desired, see documentation here: - # https://github.com/dart-lang/setup-dart/blob/main/README.md - # - uses: dart-lang/setup-dart@v1 - - uses: dart-lang/setup-dart@9a04e6d73cca37bd455e0608d7e5092f881fd603 - - - name: Install dependencies - run: dart pub get - - - name: Verify formatting - run: dart format --output=none --set-exit-if-changed . - - # Consider passing '--fatal-infos' for slightly stricter analysis. - - name: Analyze project source - run: dart analyze - - # Your project will need to have tests in test/ and a dependency on - # package:test for this step to succeed. Note that Flutter projects will - # want to change this to 'flutter test'. - - name: Run tests - run: dart test diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..14b837b --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,34 @@ +name: Publish to pub.dev + +on: + push: + tags: + - '[0-9]+.[0-9]+.[0-9]+*' + +jobs: + pana: + name: Pana Analysis + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Dart + uses: dart-lang/setup-dart@v1 + + - name: Install dependencies + run: dart pub get --no-example + + - name: Activate pana + run: dart pub global activate pana + + - name: Run pana + run: pana --exit-code-threshold 150 . + + publish: + needs: pana + name: Publish Package + permissions: + id-token: write # Required for authentication using OIDC + uses: dart-lang/setup-dart/.github/workflows/publish.yml@v1 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..f08e3a0 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,31 @@ +name: Dart Test + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + # Note: This workflow uses the latest stable version of the Dart SDK. + # You can specify other versions if desired, see documentation here: + # https://github.com/dart-lang/setup-dart/blob/main/README.md + - uses: dart-lang/setup-dart@v1 + + - name: Install dependencies + run: dart pub get --no-example + + - name: Verify formatting + run: dart format --output=none --set-exit-if-changed lib/ + + - name: Analyze project source + run: dart analyze lib/ + + - name: Run tests + run: dart test diff --git a/.gitignore b/.gitignore index 20bbbd4..d625217 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ pubspec.lock .idea/ +.DS_Store diff --git a/CHANGELOG.md b/CHANGELOG.md index 505df8b..ec4bb01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,14 @@ +## 1.2.0 + +- Implemented `compareInverse` comparator for sorting in an inverse order as an alternative for `package:collection`'s + `inverse`, as the latter requires `compare` to have an explicit generic. + ## 1.1.0 - Implemented `sortSequentially` comparator as a concise alternative to comparator chaining with `then`; - Added `package:collection` dependency; -- **Marked `then`, `reversed` Comparator extension methods and `compareTransformed` function as deprecated - they -duplicate existing functionality of `package:collection`.** +- **Marked `then`, `reversed` Comparator extension methods and `compareTransformed` function as deprecated - they + duplicate existing functionality of `package:collection`.** ## 1.0.0 diff --git a/README.md b/README.md index ee5753b..860765b 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,12 @@ Comparison by a single field: users.sort(compare((u) => u.username)); ``` +Comparison by a single field in an inverse order: +```dart +// this will sort the list by the username field of the User object +users.sort(compareInverse((u) => u.username)); +``` + Comparison by a boolean field: ```dart users.sort(compareBool((u) => u.isActive)); diff --git a/lib/src/field_comparators.dart b/lib/src/field_comparators.dart index 0735b35..97b0873 100644 --- a/lib/src/field_comparators.dart +++ b/lib/src/field_comparators.dart @@ -34,47 +34,58 @@ Comparator compareTransformed( Comparator _compareTransformed( FieldExtractor fieldExtractor, ComparableTransformer comparableTransformer, -) { - return (a, b) { - final valueA = comparableTransformer(fieldExtractor(a)); - final valueB = comparableTransformer(fieldExtractor(b)); +) => + (a, b) { + final valueA = comparableTransformer(fieldExtractor(a)); + final valueB = comparableTransformer(fieldExtractor(b)); - return Comparable.compare(valueA, valueB); - }; -} + return Comparable.compare(valueA, valueB); + }; -/// Returns a [Comparator] of type [T] comparing by the [Comparable] field of -/// type [R] extracted with the [fieldExtractor]. +/// Returns a [Comparator] of type [T] comparing by the [Comparable] field extracted with the [fieldExtractor]. /// /// Example: /// ```dart /// // compare by a single field /// users.sort(compare((u) => u.name)); /// -/// // compare by multiple fields with comparator chaining -/// // unfortunately, Dart cannot infer chained types correctly, so it is -/// // required to provide the type parameters explicitly +/// // compare by multiple fields with `compareSequentially` /// users.sort( -/// compare((user) => user.name).then( -/// compare((user) => user.surname).then( -/// compare((user) => user.country), -/// ), +/// compareSequentially( +/// compare((user) => user.name), +/// compare((user) => user.surname), +/// compare((user) => user.country) /// ), /// ); /// ``` -Comparator compare(FieldExtractor fieldExtractor) { - return _compareTransformed( - fieldExtractor, identityTransformer); -} +Comparator compare(FieldExtractor fieldExtractor) => + _compareTransformed( + fieldExtractor, + identityTransformer, + ); + +/// Returns a [Comparator] of type [T] comparing by the [Comparable] field extracted with the [fieldExtractor], +/// in an inverse order. +/// Identical to calling `compare` with the arguments swapped: +/// ```dart +/// (a, b) => compare(...).call(b, a); +/// ``` +/// +/// Example: +/// ```dart +/// // compare by a single field in an inverse order +/// users.sort(compareInverse((u) => u.name)); +/// ``` +Comparator compareInverse(FieldExtractor fieldExtractor) => + (a, b) => compare(fieldExtractor).call(b, a); /// Returns a comparator for a boolean field extracted with the given /// [fieldExtractor]. /// -/// Internally it will use the integer comparison and the following +/// Internally, it will use integer comparison and the following /// transformation: `true => 1, false => 0`. -Comparator compareBool(FieldExtractor fieldExtractor) { - return _compareTransformed(fieldExtractor, boolTransformer); -} +Comparator compareBool(FieldExtractor fieldExtractor) => + _compareTransformed(fieldExtractor, boolTransformer); /// Returns a comparator that will compare the values of [T] using the [comparators] in their order in the iterable. /// Next comparators are used as tie breakers for the previous ones. diff --git a/pubspec.yaml b/pubspec.yaml index c47c873..4f0b279 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: comparators description: A package providing flexible Java-like comparator generators, featuring declarative comparator combining. -version: 1.1.0 +version: 1.2.0 repository: https://github.com/mitryp/comparators issue_tracker: https://github.com/mitryp/comparators/issues @@ -8,11 +8,11 @@ environment: sdk: '>=2.17.0 <4.0.0' dependencies: - collection: ^1.18.0 + collection: ^1.19.0 dev_dependencies: - lints: ^2.0.0 - test: ^1.21.0 + lints: ^6.0.0 + test: ^1.28.0 test_utils: git: url: 'https://github.com/mitryp/flutter_utils' diff --git a/test/comparators_test.dart b/test/comparators_test.dart index c627265..4eafc3b 100644 --- a/test/comparators_test.dart +++ b/test/comparators_test.dart @@ -7,7 +7,7 @@ import 'package:test/scaffolding.dart'; import 'package:test_utils/test_utils.dart'; import 'not_comparable.dart'; -import 'utils.dart'; +import 'utils.dart' as utils; void main() { final rand = Random(); @@ -15,7 +15,7 @@ void main() { group('Utils tests', () { test( '`identityTransformer` works correctly', - () => repeat(times: testRuns, () { + () => repeat(times: utils.testRuns, () { final i = rand.nextInt(99999); expect(identityTransformer(i), equals(i)); @@ -24,7 +24,7 @@ void main() { test( '`boolTransformer` works correctly', - () => repeat(times: testRuns, () { + () => repeat(times: utils.testRuns, () { final b = rand.nextBool(); final matcher = [false, true].indexOf(b); @@ -36,8 +36,8 @@ void main() { group('Comparators tests', () { test( '`compareTransformed` works correctly', - () => repeat(times: testRuns, () { - final list = rList(rand); + () => repeat(times: utils.testRuns, () { + final list = utils.rList(rand); // ignore: deprecated_member_use_from_same_package final comparator = compareTransformed( (nc) => nc, @@ -46,38 +46,54 @@ void main() { list.sort(comparator); - expect(isSorted(list, comparator: comparator), isTrue); + expect(utils.isSorted(list, comparator: comparator), isTrue); }), ); test( '`compare` works correctly', - () => repeat(times: testRuns, () { - final list = rList(rand); + () => repeat(times: utils.testRuns, () { + final list = utils.rList(rand); final comparator = compare((nc) => nc.intValue); list.sort(comparator); - expect(isSorted(list, comparator: comparator), isTrue); + expect(utils.isSorted(list, comparator: comparator), isTrue); + }), + ); + + test( + '`compareInverse` works correctly', + () => repeat(times: utils.testRuns, () { + final list = utils.rList(rand); + final comparator = compareInverse((nc) => nc.intValue); + final originalComparator = compare((nc) => nc.intValue); + + list.sort(comparator); + + expect( + utils.isSorted(list, comparator: (a, b) => originalComparator(b, a)), + isTrue, + ); }), ); test( '`compareBool` works correctly', - () => repeat(times: testRuns, () { - final list = rList(rand); + () => repeat(times: utils.testRuns, () { + final list = utils.rList(rand); final comparator = compareBool((nc) => nc.boolValue); list.sort(comparator); - expect(isSorted(list, comparator: comparator), isTrue); + expect(utils.isSorted(list, comparator: comparator), isTrue); }), ); test( '`compareSequentially` works correctly', - () => repeat(times: testRuns, () { - final list = rList(rand); + () => repeat(times: utils.testRuns, () { + final list = utils.rList(rand); final matcher = [...list]; final comparator = compareSequentially([ @@ -86,7 +102,7 @@ void main() { ]); list.sort(comparator); - matcher.sort(matcherComparator); + matcher.sort(utils.matcherComparator); expect(list, orderedEquals(matcher)); }), diff --git a/test/utils.dart b/test/utils.dart index 41339b2..460b88a 100644 --- a/test/utils.dart +++ b/test/utils.dart @@ -6,8 +6,10 @@ const testRuns = 1000; const randomListLength = 20; /// Checks whether the given [list] of [NotComparable] is sorted with the given [comparator]. -bool isSorted(List list, - {required Comparator comparator}) { +bool isSorted( + List list, { + required Comparator comparator, +}) { for (var i = 0; i < list.length - 1; i++) { if (comparator(list[i], list[i + 1]) > 0) return false; }