Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 0 additions & 41 deletions .github/workflows/dart.yml

This file was deleted.

34 changes: 34 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -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
31 changes: 31 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
pubspec.lock

.idea/
.DS_Store
9 changes: 7 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
59 changes: 35 additions & 24 deletions lib/src/field_comparators.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,47 +34,58 @@ Comparator<T> compareTransformed<T, R>(
Comparator<T> _compareTransformed<T, R>(
FieldExtractor<T, R> fieldExtractor,
ComparableTransformer<R, Comparable> 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) => user.name).then(
/// compare<User>((user) => user.surname).then(
/// compare<User>((user) => user.country),
/// ),
/// compareSequentially(
/// compare((user) => user.name),
/// compare((user) => user.surname),
/// compare((user) => user.country)
/// ),
/// );
/// ```
Comparator<T> compare<T>(FieldExtractor<T, Comparable> fieldExtractor) {
return _compareTransformed<T, Comparable>(
fieldExtractor, identityTransformer);
}
Comparator<T> compare<T>(FieldExtractor<T, Comparable> fieldExtractor) =>
_compareTransformed<T, Comparable>(
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<T> compareInverse<T>(FieldExtractor<T, Comparable> 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<T> compareBool<T>(FieldExtractor<T, bool> fieldExtractor) {
return _compareTransformed(fieldExtractor, boolTransformer);
}
Comparator<T> compareBool<T>(FieldExtractor<T, bool> 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.
Expand Down
8 changes: 4 additions & 4 deletions pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
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

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'
Expand Down
46 changes: 31 additions & 15 deletions test/comparators_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ 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();

group('Utils tests', () {
test(
'`identityTransformer` works correctly',
() => repeat(times: testRuns, () {
() => repeat(times: utils.testRuns, () {
final i = rand.nextInt(99999);

expect(identityTransformer(i), equals(i));
Expand All @@ -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);

Expand All @@ -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<NotComparable, NotComparable>(
(nc) => nc,
Expand All @@ -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<NotComparable>((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<NotComparable>((nc) => nc.intValue);
final originalComparator = compare<NotComparable>((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<NotComparable>((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<NotComparable>([
Expand All @@ -86,7 +102,7 @@ void main() {
]);

list.sort(comparator);
matcher.sort(matcherComparator);
matcher.sort(utils.matcherComparator);

expect(list, orderedEquals(matcher));
}),
Expand Down
6 changes: 4 additions & 2 deletions test/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<NotComparable> list,
{required Comparator<NotComparable> comparator}) {
bool isSorted(
List<NotComparable> list, {
required Comparator<NotComparable> comparator,
}) {
for (var i = 0; i < list.length - 1; i++) {
if (comparator(list[i], list[i + 1]) > 0) return false;
}
Expand Down