Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add extension for nullable Iterable #16

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
## [2.0.5] - 11/03/21
- `delay` await 3.seconds.delay(() { some code }
- `added getTextSize` - A good use-case will be drawing a divider with the exect width of the text above it.
- `isVideo` - Checks if string is an video file.
- `isAudio` - Checks if string is an audio file.
- `isImage` - Checks if string is an image file.
- `isNumericOnly` - Check if the string has any number in it.
- `isAlphabetOnly` - Checks if string consist only Alphabet. (No Whitespace)
- `hasCapitalletter` - Checks if string contains at least one Capital Letter.
- `isHTML` - Checks if string is an html file.
- `isEmail` - Checks if string is email..
- `isPhoneNumber` - Checks if string is phone number, good for login checks.
- `isUsername` - Checks if string is a valid username, good for login checks.
- `isCurrency` - Checks if string is Currency.
- `isPalindrom` - Checks if string is Palindrom. (good to know for interviews as well)


## [2.0.0] - 11/03/21
- `Support flutter 2`
- `Support null safety`
Expand Down
26 changes: 16 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@

[![](https://img.shields.io/badge/ver-2.0.1-brightgreen)](https://github.com/droididan/dart_extentions) ![](https://img.shields.io/badge/Code%20Coverage-96%25-green) ![](https://img.shields.io/badge/Bitrise-Pass-green)
[![](https://img.shields.io/badge/ver-2.0.5-green)](https://github.com/droididan/dart_extentions) ![](https://img.shields.io/badge/Code%20Coverage-96%25-green) ![](https://img.shields.io/badge/Bitrise-Pass-green)


## What New
* `SizedBox` extension - convert to percents 💪🏻
* `Sound null-safety` added! 💪🏻
* `BuildContext` extensions 💪🏻
* `List<Widget>` extensions 💪🏻
* `Text` extensions 💪🏻
* `Icon` extensions 💪🏻
* `.sortBy` [0.3.5] Sorts elements in the array in-place according to natural sort order of the value returned by specified selector function.
* `.withTooltip` Tooltips improve the accessibility of visual widgets by proving a textual representation of the widget

* `Iterables` -> `all` -> Returns `true` if all elements match the given
* `isVideo` - Checks if string is an video file.
* `isAudio` - Checks if string is an audio file.
* `isImage` - Checks if string is an image file.
* `isNumericOnly` - Check if the string has any number in it.
* `isAlphabetOnly` - Checks if string consist only Alphabet. (No Whitespace)
* `hasCapitalletter` - Checks if string contains at least one Capital Letter.
* `isHTML` - Checks if string is an html file.
* `isEmail` - Checks if string is email..
* `isPhoneNumber` - Checks if string is phone number, good for login checks.
* `isUsername` - Checks if string is a valid username, good for login checks.
* `isCurrency` - Checks if string is Currency.
* `isPalindrom` - Checks if string is Palindrom. (good to know for interviews

as well)
Why Method Extensions? When you’re using someone else’s API or when you implement a library that’s widely used, it’s often impractical or impossible to change the API. But you might still want to add some functionality.

*let me know if you want something specific or you found a bug at bar.idan@gmail.com*
Expand Down
2 changes: 1 addition & 1 deletion lib/dart_extensions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,6 @@ export 'package:dart_extensions/src/iterable.dart';
export 'package:dart_extensions/src/ranges.dart';
export 'package:dart_extensions/src/search_algorithms.dart';
export 'package:dart_extensions/src/sort_algorithms.dart';
export 'package:dart_extensions/src/strings.dart';
export 'package:dart_extensions/src/string_ext.dart';
export 'package:dart_extensions/src/flutter/widgets.dart';

13 changes: 13 additions & 0 deletions lib/emum.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
extension EnumExt on dynamic {
static bool _isEnumItem(enumItem) {
final splitEnum = enumItem.toString().split('.');
return splitEnum.length > 1 && splitEnum[0] == enumItem.runtimeType.toString();
}

String convertToString({bool camelCase = false}) {
assert(this != null);
assert(_isEnumItem(this), '$this of type ${this.runtimeType.toString()} is not an enum item');
final temp = this.toString().split('.')[1];
return !camelCase ? temp : camelCaseToWords(temp);
}
}
14 changes: 14 additions & 0 deletions lib/src/flutter/duration.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import 'dart:async';

extension DurationExt on Duration {
/// Utility to delay some callback (or code execution).
///
/// Sample:
/// ```
/// await 3.seconds.delay(() {
/// ....
/// }
///
///```
Future delay([FutureOr callback()?]) async => Future.delayed(this, callback);
}
9 changes: 4 additions & 5 deletions lib/src/http.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@
*/

import 'dart:convert' as convert;

import 'package:dart_extensions/src/strings.dart';
import '../src/string_ext.dart';
import 'package:http/http.dart' as http;

const _defaultHeaders = {"Content-type": "application/json"};
Expand Down Expand Up @@ -46,7 +45,7 @@ extension HttpExtensions on String {
/// current string is www.mydomain.com
/// endpoint param - user
/// result request -> www.mydomain.com/user
Future<dynamic> httpPost(String endPoint,String json, [Map<String, String> headers = _defaultHeaders]) async {
Future<dynamic> httpPost(String endPoint, String json, [Map<String, String> headers = _defaultHeaders]) async {
if (this.isEmptyOrNull) return;

try {
Expand All @@ -66,7 +65,7 @@ extension HttpExtensions on String {
/// current string is www.mydomain.com
/// endpoint param - user
/// result request -> www.mydomain.com/user
Future<dynamic> httpPut(String endPoint,String json, [Map<String, String> headers = _defaultHeaders]) async {
Future<dynamic> httpPut(String endPoint, String json, [Map<String, String> headers = _defaultHeaders]) async {
if (this.isEmptyOrNull) return;

try {
Expand All @@ -86,7 +85,7 @@ extension HttpExtensions on String {
/// current string is www.mydomain.com
/// endpoint param - user
/// result request -> www.mydomain.com/user
Future<dynamic> httpDelete(String endPoint,{Map<String, String>? headers}) async {
Future<dynamic> httpDelete(String endPoint, {Map<String, String>? headers}) async {
if (this.isEmptyOrNull) return;

try {
Expand Down
1 change: 1 addition & 0 deletions lib/src/int.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ extension IntExtensions on int {
return this;
}

// ignore: unnecessary_null_comparison
bool get isNull => (this == null);

/// Returns the absolute value
Expand Down
141 changes: 98 additions & 43 deletions lib/src/iterable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,53 @@ import 'package:quiver/iterables.dart';
import 'data_stractures/stack.dart';
import 'equality.dart';

extension CollectionsExtensions<T> on Iterable<T> {

extension CollectionsNullableExtension<T> on Iterable<T>? {

/// Returns this Iterable if it's not `null` and the empty list otherwise.
Iterable<T> orEmpty() => this ?? Iterable<T>.empty();

///Returns `true` if this nullable iterable is either null or empty.
bool get isEmptyOrNull => this?.isEmpty ?? true;

/// Returns `true` if at least one element matches the given [predicate].
bool any(bool predicate(T element)) {
if (this.isEmptyOrNull) return false;
for (final element in this.orEmpty()) if (predicate(element)) return true;
return false;
}

/// Return a list concatenates the output of the current list and another [iterable]
List<T> concatWithSingleList(Iterable<T> iterable) {
if (isEmptyOrNull || iterable.isEmptyOrNull) return [];

return <T>[...this.orEmpty(), ...iterable];
}

/// Return a list concatenates the output of the current list and multiple [iterables]
List<T> concatWithMultipleList(List<Iterable<T>> iterables) {
if (isEmptyOrNull || iterables.isEmptyOrNull) return [];
final list = iterables.toList(growable: false).expand((i) => i);
return <T>[...this.orEmpty(), ...list];
}

/// Zip is used to combine multiple iterables into a single list that contains
/// the combination of them two.
zip<T>(Iterable<T> iterable) sync* {
if (iterable.isEmptyOrNull) return;
final iterables = List<Iterable>.empty(growable: true)
..add(this.orEmpty())
..add(iterable);

final iterators = iterables.map((e) => e.iterator).toList(growable: false);
while (iterators.every((e) => e.moveNext())) {
yield iterators.map((e) => e.current).toList(growable: false);
}
}
}

typedef IndexedPredicate<T> = bool Function(int index, T);

extension CollectionsExtensions<T> on Iterable<T> {
///Sorts elements in the array in-place according to natural sort order of the value returned by specified [selector] function.
Iterable<T> sortBy<TKey>(
TKey Function(T) keySelector, {
Expand All @@ -29,13 +73,6 @@ extension CollectionsExtensions<T> on Iterable<T> {
return InternalOrderedIterable(this, keySelector, keyComparer, false);
}

/// Returns `true` if at least one element matches the given [predicate].
bool any(bool predicate(T element)) {
if (this.isEmptyOrNull) return false;
for (final element in this) if (predicate(element)) return true;
return false;
}

/// Convert iterable to set
Set<T> toMutableSet() => Set.from(this);

Expand Down Expand Up @@ -91,7 +128,7 @@ extension CollectionsExtensions<T> on Iterable<T> {
List<T> takeOnly(int n) {
if (n == 0) return [];

var list = List<T>.empty();
var list = List<T>.empty(growable: true);
var thisList = this.toList();
if (this is Iterable) {
final resultSize = this.length - n;
Expand All @@ -109,7 +146,7 @@ extension CollectionsExtensions<T> on Iterable<T> {
List<T> drop(int n) {
if (n == 0) return [];

var list = List<T>.empty();
var list = List<T>.empty(growable: true);
var originalList = this.toList();
if (this is Iterable) {
final resultSize = this.length - n;
Expand All @@ -124,7 +161,7 @@ extension CollectionsExtensions<T> on Iterable<T> {
}

// Retuns map operation as a List
List<E> mapList<E>(E f(T e)) => this.map(f).toList();
List<E> mapList<E>(E f(T e)) => this.map(f).toList();

// Takes the first half of a list
List<T> firstHalf() => take(halfLength).toList();
Expand Down Expand Up @@ -170,6 +207,9 @@ extension CollectionsExtensions<T> on Iterable<T> {
/// var name = [].firstOrDefault["jack"]; // jack
T firstOrDefault(T defaultValue) => firstOrNull ?? defaultValue;

/// Will retrun new [Iterable] with all elements that satisfy the predicate [predicate],
Iterable<T> whereIndexed(IndexedPredicate<T> predicate) => _IndexedWhereIterable(this, predicate);

///
/// Performs the given action on each element on iterable, providing sequential index with the element.
/// [item] the element on the current iteration
Expand Down Expand Up @@ -229,6 +269,17 @@ extension CollectionsExtensions<T> on Iterable<T> {
return count;
}

/// Returns `true` if all elements match the given predicate.
/// Example:
/// [5, 19, 2].all(isEven), isFalse)
/// [6, 12, 2].all(isEven), isTrue)
bool all(bool predicate(T pred)?) {
for (var e in this) {
if (!predicate!(e)) return false;
}
return true;
}

/// Returns a list containing only the elements from given collection having distinct keys.
///
/// Basically it's just like distinct function but with a predicate
Expand All @@ -250,7 +301,7 @@ extension CollectionsExtensions<T> on Iterable<T> {
/// 36 Ran
List<T> distinctBy(predicate(T selector)) {
final set = HashSet();
final list = List<T>.empty();
final List<T> list = [];
toList().forEach((e) {
final key = predicate(e);
if (set.add(key)) {
Expand Down Expand Up @@ -308,20 +359,6 @@ extension CollectionsExtensions<T> on Iterable<T> {
return stack;
}

bool get isEmptyOrNull => this == null || isEmpty;

/// Zip is used to combine multiple iterables into a single list that contains
/// the combination of them two.
zip<T>(Iterable<T> iterable) sync* {
if (iterable.isEmptyOrNull) return;
final iterables = List<Iterable>.empty()..add(this)..add(iterable);

final iterators = iterables.map((e) => e.iterator).toList(growable: false);
while (iterators.every((e) => e.moveNext())) {
yield iterators.map((e) => e.current).toList(growable: false);
}
}

/// Splits the Iterable into chunks of the specified size
///
/// example:
Expand All @@ -330,23 +367,8 @@ extension CollectionsExtensions<T> on Iterable<T> {
/// ([1, 2, 3], [4, 5, 6], [7, 8, 9], [10])
Iterable<List<T>> chunks(int size) => partition(this, size);

/// Return a list concatenates the output of the current list and another [iterable]
List<T> concatWithSingleList(Iterable<T> iterable) {
if (isEmptyOrNull || iterable.isEmptyOrNull) return [];

return <T>[...this, ...iterable];
}

/// Return a list concatenates the output of the current list and multiple [iterables]
List<T> concatWithMultipleList(List<Iterable<T>> iterables) {
if (isEmptyOrNull || iterables.isEmptyOrNull) return [];
final list = iterables.toList(growable: false).expand((i) => i);
return <T>[...this, ...list];
}

/// Creates a Map instance in which the keys and values are computed from the iterable.
Map<dynamic, dynamic> associate(key(element), value(element)) =>
Map.fromIterable(this, key: key, value: value);
Map<dynamic, dynamic> associate(key(element), value(element)) => Map.fromIterable(this, key: key, value: value);

/// Returns the first element matching the given [predicate], or `null`
/// if element was not found.
Expand All @@ -360,3 +382,36 @@ extension CollectionsExtensions<T> on Iterable<T> {
return null;
}
}

// A lazy [Iterable] skip elements do **NOT** match the predicate [_f].
class _IndexedWhereIterable<E> extends Iterable<E> {
final Iterable<E> _iterable;
final IndexedPredicate<E> _f;

_IndexedWhereIterable(this._iterable, this._f);

@override
Iterator<E> get iterator => _IndexedWhereIterator<E>(_iterable.iterator, _f);
}

/// [Iterator] for [_IndexedWhereIterable]
class _IndexedWhereIterator<E> extends Iterator<E> {
final Iterator<E> _iterator;
final IndexedPredicate<E> _f;
int _index = 0;

_IndexedWhereIterator(this._iterator, this._f);

@override
bool moveNext() {
while (_iterator.moveNext()) {
if (_f(_index++, _iterator.current)) {
return true;
}
}
return false;
}

@override
E get current => _iterator.current;
}
13 changes: 13 additions & 0 deletions lib/src/model/user.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,19 @@ class User {

User(this.age,this.name);


@override
bool operator ==(other) {
return (other is User)
&& other.name == name
&& other.age == age;
}


@override
int get hashCode => age.hashCode ^ name.hashCode;


@override
String toString() {
return "$age, $name";
Expand Down
Loading