Skip to content

Commit

Permalink
Add more collection and types extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
ashtanko committed Jun 27, 2024
1 parent b21db7d commit 28c892f
Show file tree
Hide file tree
Showing 7 changed files with 253 additions and 36 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## 0.1.6
- Add `filterNotNullTo`, `filterNotNull`, `listOfNotNull`, `ifNull` functions and extensions
- Add unit tests
- Fix code documentation

## 0.1.5
- Fix code documentation.
- Deprecate `notEmpty`, `executeIf`, 'unwrapped' methods.
Expand Down
9 changes: 0 additions & 9 deletions example/nullx_example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import 'package:nullx/nullx.dart';

void main() {
/// Variables
///
///
// ignore: unnecessary_nullable_for_final_variable_declarations
const int? nullableInt = 10;
Expand All @@ -27,8 +25,6 @@ void main() {
const List<String?>? emptyStringList = [];

/// Collections
///
///
// Maps over the list, applying a function to each non-null element,
nullableIntList?.mapNonNull((item) => item * 2); // prints: [2, 6]
Expand Down Expand Up @@ -74,9 +70,6 @@ void main() {
nullableBool.or(defaultValue: true); // Outputs: true

/// Types
///
///
// Unwraps the nullable string and performs an operation on it
callWhen(
condition: () => userAge >= 18,
Expand Down Expand Up @@ -177,8 +170,6 @@ void main() {
nullBool.or(defaultValue: true); // Outputs: true

/// Utils
///
///
// Tries to cast the dynamic value to a String
final String? name = tryCast<String>(value);
Expand Down
86 changes: 71 additions & 15 deletions lib/src/collections.dart
Original file line number Diff line number Diff line change
Expand Up @@ -87,25 +87,81 @@ extension WhatIfNotNullOrEmptyExtension<T> on List<T>? {
}
}

/// Extension on `Iterable<T>?` to add an `isNullOrEmpty` getter.
/// Extension on `Iterable<T>?`.
extension CollectionExtensions<T> on Iterable<T?>? {
///
/// This extension provides a convenient way to check if an iterable is null
/// or empty.
/// The `isNullOrEmpty` getter returns true if the iterable is null or if it
/// is empty.
///
/// Example usage:
///
/// ```dart
/// List<int>? nullableList = [1, 2, 3];
/// print(nullableList.isNullOrEmpty); // prints: false
///
/// nullableList = null;
/// print(nullableList.isNullOrEmpty); // prints: true
///
/// nullableList = [];
/// print(nullableList.isNullOrEmpty); // prints: true
/// ```
bool get isNullOrEmpty => this == null || this!.isEmpty;

/// Filters non-null elements of the iterable and adds them to the
/// destination list.
///
/// The [destination] list should be a list where the non-null elements will
/// be added.
///
/// Returns the destination list with the non-null elements added.
///
/// If the iterable is null or empty, it returns the original
/// destination list.
///
/// Example usage:
///
/// ```dart
/// var list = [1, null, 3, null];
/// var destination = <int>[];
/// list.filterNotNullTo(destination);
/// print(destination); // prints: [1, 3]
/// ```
C filterNotNullTo<C extends List<T>>(C destination) {
if (this.isNullOrEmpty) return destination;
for (final element in this!) {
if (element != null) {
destination.add(element);
}
}
return destination;
}

/// Filters non-null elements of the iterable
Iterable<T> filterNotNull() {
return filterNotNullTo(<T>[]);
}
}

/// Creates a new list containing all non-null elements from the provided list.
///
/// This extension provides a convenient way to check if an iterable is null
/// or empty.
/// The `isNullOrEmpty` getter returns true if the iterable is null or if it
/// is empty.
/// The [elements] list should be a list that may contain null elements.
///
/// Example usage:
/// The [growable] parameter determines whether the returned list is growable.
/// If [growable] is true, the returned list is growable.
/// If [growable] is false, the returned list is fixed-length.
///
/// ```dart
/// List<int>? nullableList = [1, 2, 3];
/// print(nullableList.isNullOrEmpty); // prints: false
/// Returns a new list with all non-null elements from the original list.
/// If the original list is null or empty, it returns an empty list.
///
/// nullableList = null;
/// print(nullableList.isNullOrEmpty); // prints: true
/// Example usage:
///
/// nullableList = [];
/// print(nullableList.isNullOrEmpty); // prints: true
/// ```dart
/// var list = [1, null, 3, null];
/// var newList = listOfNotNull(list);
/// print(newList); // prints: [1, 3]
/// ```
extension CollectionExtensions<T> on Iterable<T>? {
bool get isNullOrEmpty => this == null || this!.isEmpty;
List<T> listOfNotNull<T>(List<T?> elements, {bool growable = true}) {
return elements.filterNotNull().toList(growable: growable);
}
19 changes: 19 additions & 0 deletions lib/src/types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,25 @@ extension ConditionExtension<T> on T? {
return isFalse();
}
}

/// Executes the [action] if the value is null.
///
/// This method is useful when you need to perform an action when a value is null.
/// The [action] should be a function that takes no arguments and returns void.
///
/// Returns the original value after executing the [action] if it was null.
///
/// Example usage:
///
/// ```dart
/// var nullableInt = null;
/// nullableInt = nullableInt.ifNull(() => print('Value was null'));
/// // prints: 'Value was null'
/// ```
T? ifNull(void Function() action) {
if (this == null) action();
return this;
}
}

/// Extension on `T` to add `takeIf` and `takeUnless` methods.
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: nullx
homepage: https://shtanko.dev
description: >-
nullx is a collection of elegant extensions for handling null types in Dart.
version: 0.1.5
version: 0.1.6
repository: https://github.com/ashtanko/nullx

topics:
Expand Down
126 changes: 115 additions & 11 deletions test/collections_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -80,21 +80,125 @@ void main() {
});

group('CollectionExtensions', () {
test('isNullOrEmpty returns true if list is null', () {
const List<int>? list = null;
expect(list.isNullOrEmpty, isTrue);
group('isNullOrEmpty', () {
test('isNullOrEmpty returns true if list is null', () {
const List<int>? list = null;
expect(list.isNullOrEmpty, isTrue);
});

test('isNullOrEmpty returns true if list is empty', () {
// ignore: unnecessary_nullable_for_final_variable_declarations
final List<int>? list = [];
expect(list.isNullOrEmpty, isTrue);
});

test('isNullOrEmpty returns false if list is not empty', () {
// ignore: unnecessary_nullable_for_final_variable_declarations
final List<int>? list = [1, 2, 3];
expect(list.isNullOrEmpty, isFalse);
});
});

test('isNullOrEmpty returns true if list is empty', () {
// ignore: unnecessary_nullable_for_final_variable_declarations
final List<int>? list = [];
expect(list.isNullOrEmpty, isTrue);
group('filterNotNullTo', () {
test('adds non-null elements to destination', () {
final source = [1, null, 3, null];
final destination = <int>[];
source.filterNotNullTo(destination);
expect(destination, equals([1, 3]));
});

test('adds non-null elements to destination with result', () {
final source = [1, null, 3, null];
final destination = <int>[];
final res = source.filterNotNullTo(destination);
expect(destination, equals([1, 3]));
expect(res, equals([1, 3]));
});

test('returns the same destination if source is empty', () {
final source = <int?>[];
final destination = <int>[];
source.filterNotNullTo(destination);
expect(destination, isEmpty);
});

test('returns the same destination if source is null', () {
const List<int?>? source = null;
final destination = <int>[];
source.filterNotNullTo(destination);
expect(destination, isEmpty);
});

test('does not add null elements to destination', () {
final source = <int?>[null, null, null];
final destination = <int>[];
source.filterNotNullTo(destination);
expect(destination, isEmpty);
});
});

test('isNullOrEmpty returns false if list is not empty', () {
// ignore: unnecessary_nullable_for_final_variable_declarations
final List<int>? list = [1, 2, 3];
expect(list.isNullOrEmpty, isFalse);
group('filterNotNull', () {
test('filters non-null elements from iterable', () {
final source = [1, null, 3, null];
final result = source.filterNotNull();
expect(result, equals([1, 3]));
});

test('returns an empty iterable if source is empty', () {
final source = <int?>[];
final result = source.filterNotNull();
expect(result, isEmpty);
});

test('returns an empty iterable if source is null', () {
const List<int?>? source = null;
final result = source.filterNotNull();
expect(result, isEmpty);
});

test('does not include null elements in the result', () {
final source = <int?>[null, null, null];
final result = source.filterNotNull();
expect(result, isEmpty);
});
});
});

group('listOfNotNull', () {
test('filters non-null elements from list', () {
final source = [1, null, 3, null];
final result = listOfNotNull(source);
expect(result, equals([1, 3]));
});

test('returns an empty list if source is empty', () {
final source = <int?>[];
final result = listOfNotNull(source);
expect(result, isEmpty);
});

test('does not include null elements in the result', () {
final source = <int?>[null, null, null];
final result = listOfNotNull(source);
expect(result, isEmpty);
});

test('returns a growable list if growable is true', () {
final source = [1, null, 3, null];
final result = listOfNotNull(source);
expect(result, equals([1, 3]));
result.add(4);
expect(result, equals([1, 3, 4])); // check if list is growable
});

test('returns a fixed-length list if growable is false', () {
final source = [1, null, 3, null];
final result = listOfNotNull(source, growable: false);
expect(result, equals([1, 3]));
expect(
() => result.add(4),
throwsUnsupportedError,
); // check if list is fixed-length
});
});
}
42 changes: 42 additions & 0 deletions test/types_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,48 @@ void main() {
});
});

group('ifNull', () {
test('ifNull calls action when the value is null', () {
const int? value = null;
bool wasCalled = false;

value.ifNull(() {
wasCalled = true;
});

expect(wasCalled, isTrue);
});

test('ifNull does not call action when the value is not null', () {
// ignore: unnecessary_nullable_for_final_variable_declarations
const int? value = 10;
bool wasCalled = false;

value.ifNull(() {
wasCalled = true;
});

expect(wasCalled, isFalse);
});

test('ifNull returns the original value when the value is null', () {
const int? value = null;

final result = value.ifNull(() {});

expect(result, isNull);
});

test('ifNull returns the original value when the value is not null', () {
// ignore: unnecessary_nullable_for_final_variable_declarations
const int? value = 10;

final result = value.ifNull(() {});

expect(result, equals(10));
});
});

test('conditionNotNullWith - not null', () {
// ignore: unnecessary_nullable_for_final_variable_declarations
const String? nullableString = 'notNull';
Expand Down

0 comments on commit 28c892f

Please sign in to comment.