From c77857dc864bd6018ae4aa240d882bea2e785952 Mon Sep 17 00:00:00 2001 From: Vincenzo Sarnataro <> Date: Fri, 26 Mar 2021 15:39:56 +0100 Subject: [PATCH 01/11] add extensions for nullable Iterable add orEmpty extensions --- lib/src/iterable.dart | 83 +++++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 38 deletions(-) diff --git a/lib/src/iterable.dart b/lib/src/iterable.dart index 364bd1a..d18c0bb 100644 --- a/lib/src/iterable.dart +++ b/lib/src/iterable.dart @@ -18,9 +18,51 @@ import 'package:quiver/iterables.dart'; import 'data_stractures/stack.dart'; import 'equality.dart'; -extension CollectionsExtensions on Iterable { - +extension CollectionsNullableExtension on Iterable? { + + /// Returns this Iterable if it's not `null` and the empty list otherwise. + Iterable orEmpty() => this ?? Iterable.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 concatWithSingleList(Iterable iterable) { + if (isEmptyOrNull || iterable.isEmptyOrNull) return []; + + return [...this.orEmpty(), ...iterable]; + } + + /// Return a list concatenates the output of the current list and multiple [iterables] + List concatWithMultipleList(List> iterables) { + if (isEmptyOrNull || iterables.isEmptyOrNull) return []; + final list = iterables.toList(growable: false).expand((i) => i); + return [...this.orEmpty(), ...list]; + } + + /// Zip is used to combine multiple iterables into a single list that contains + /// the combination of them two. + zip(Iterable iterable) sync* { + if (iterable.isEmptyOrNull) return; + final iterables = List.empty() + ..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); + } + } +} +extension CollectionsExtensions on Iterable { ///Sorts elements in the array in-place according to natural sort order of the value returned by specified [selector] function. Iterable sortBy( TKey Function(T) keySelector, { @@ -29,13 +71,6 @@ extension CollectionsExtensions on Iterable { 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 toMutableSet() => Set.from(this); @@ -124,7 +159,7 @@ extension CollectionsExtensions on Iterable { } // Retuns map operation as a List - List mapList(E f(T e)) => this.map(f).toList(); + List mapList(E f(T e)) => this.map(f).toList(); // Takes the first half of a list List firstHalf() => take(halfLength).toList(); @@ -308,20 +343,6 @@ extension CollectionsExtensions on Iterable { 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(Iterable iterable) sync* { - if (iterable.isEmptyOrNull) return; - final iterables = List.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: @@ -330,20 +351,6 @@ extension CollectionsExtensions on Iterable { /// ([1, 2, 3], [4, 5, 6], [7, 8, 9], [10]) Iterable> chunks(int size) => partition(this, size); - /// Return a list concatenates the output of the current list and another [iterable] - List concatWithSingleList(Iterable iterable) { - if (isEmptyOrNull || iterable.isEmptyOrNull) return []; - - return [...this, ...iterable]; - } - - /// Return a list concatenates the output of the current list and multiple [iterables] - List concatWithMultipleList(List> iterables) { - if (isEmptyOrNull || iterables.isEmptyOrNull) return []; - final list = iterables.toList(growable: false).expand((i) => i); - return [...this, ...list]; - } - /// Creates a Map instance in which the keys and values are computed from the iterable. Map associate(key(element), value(element)) => Map.fromIterable(this, key: key, value: value); From 856d39cfa86e8a774220501510a43a5f7ebce4a5 Mon Sep 17 00:00:00 2001 From: Vincenzo Sarnataro <> Date: Fri, 26 Mar 2021 15:40:07 +0100 Subject: [PATCH 02/11] add new test --- test/iterable_tests.dart | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/iterable_tests.dart b/test/iterable_tests.dart index 54e4eda..6d6228f 100644 --- a/test/iterable_tests.dart +++ b/test/iterable_tests.dart @@ -194,5 +194,21 @@ main() { expect(Set.from([1, 2, 3, 4]).intersect(Set.from([3, 4, 5, 6])), [1, 2, 3, 4, 5, 6]); expect(Set.from([-1, -2, -3, 4]).intersect(Set.from([3, 4, 5, 6])), [-1, -2, -3, 4, 3, 5, 6]); }); + + test('orEmpty',(){ + List? list; + expect(list.orEmpty(), List.empty()); + list= [1,2,3]; + expect(list.orEmpty(), [1,2,3]); + }); + + test('isEmptyOrNull',(){ + List? list; + expect(list.isEmptyOrNull, true); + list= []; + expect(list.isEmptyOrNull, true); + list= [1,2,3]; + expect(list.isEmptyOrNull, false); + }); }); } From abc0f5e0aca44449834d23d92f93e44d6b46c9b4 Mon Sep 17 00:00:00 2001 From: WingChan Date: Wed, 7 Apr 2021 16:41:53 +0800 Subject: [PATCH 03/11] fix: #17 --- lib/src/iterable.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/iterable.dart b/lib/src/iterable.dart index 364bd1a..f5bb8b2 100644 --- a/lib/src/iterable.dart +++ b/lib/src/iterable.dart @@ -250,7 +250,7 @@ extension CollectionsExtensions on Iterable { /// 36 Ran List distinctBy(predicate(T selector)) { final set = HashSet(); - final list = List.empty(); + final List list = []; toList().forEach((e) { final key = predicate(e); if (set.add(key)) { From 9594012edc7dfa1094a0be4c032911835b413920 Mon Sep 17 00:00:00 2001 From: Idan Ayalon Date: Fri, 23 Apr 2021 23:15:25 +0300 Subject: [PATCH 04/11] added all, enum to stirng, wrapText --- README.md | 2 ++ lib/dart_extensions.dart | 2 +- lib/emum.dart | 13 +++++++++++ lib/src/http.dart | 9 ++++---- lib/src/int.dart | 1 + lib/src/iterable.dart | 21 +++++++++++++----- lib/src/{strings.dart => string_ext.dart} | 27 ++++++++++++++++++++++- lib/src/utils.dart | 1 + pubspec.yaml | 4 ++-- test/enum_test.dart | 14 ++++++++++++ test/iterable_tests.dart | 14 +++++++++--- test/strings_test.dart | 7 +++++- 12 files changed, 96 insertions(+), 19 deletions(-) create mode 100644 lib/emum.dart rename lib/src/{strings.dart => string_ext.dart} (91%) create mode 100644 test/enum_test.dart diff --git a/README.md b/README.md index 7072a60..5a95fbd 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ ## What New +* `Strings` -> `wrapString(int afterWords)` -> break the text in 2 lines (good to format titles and stuff like that) +* `enum` -> convert enum item into text. * `SizedBox` extension - convert to percents πŸ’ͺ🏻 * `Sound null-safety` added! πŸ’ͺ🏻 * `BuildContext` extensions πŸ’ͺ🏻 diff --git a/lib/dart_extensions.dart b/lib/dart_extensions.dart index 550c59b..ee7ebdc 100644 --- a/lib/dart_extensions.dart +++ b/lib/dart_extensions.dart @@ -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'; diff --git a/lib/emum.dart b/lib/emum.dart new file mode 100644 index 0000000..a4ba19e --- /dev/null +++ b/lib/emum.dart @@ -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); + } +} diff --git a/lib/src/http.dart b/lib/src/http.dart index be8101e..8095a29 100644 --- a/lib/src/http.dart +++ b/lib/src/http.dart @@ -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"}; @@ -46,7 +45,7 @@ extension HttpExtensions on String { /// current string is www.mydomain.com /// endpoint param - user /// result request -> www.mydomain.com/user - Future httpPost(String endPoint,String json, [Map headers = _defaultHeaders]) async { + Future httpPost(String endPoint, String json, [Map headers = _defaultHeaders]) async { if (this.isEmptyOrNull) return; try { @@ -66,7 +65,7 @@ extension HttpExtensions on String { /// current string is www.mydomain.com /// endpoint param - user /// result request -> www.mydomain.com/user - Future httpPut(String endPoint,String json, [Map headers = _defaultHeaders]) async { + Future httpPut(String endPoint, String json, [Map headers = _defaultHeaders]) async { if (this.isEmptyOrNull) return; try { @@ -86,7 +85,7 @@ extension HttpExtensions on String { /// current string is www.mydomain.com /// endpoint param - user /// result request -> www.mydomain.com/user - Future httpDelete(String endPoint,{Map? headers}) async { + Future httpDelete(String endPoint, {Map? headers}) async { if (this.isEmptyOrNull) return; try { diff --git a/lib/src/int.dart b/lib/src/int.dart index e6dee5a..67339ae 100644 --- a/lib/src/int.dart +++ b/lib/src/int.dart @@ -23,6 +23,7 @@ extension IntExtensions on int { return this; } + // ignore: unnecessary_null_comparison bool get isNull => (this == null); /// Returns the absolute value diff --git a/lib/src/iterable.dart b/lib/src/iterable.dart index 364bd1a..4953d95 100644 --- a/lib/src/iterable.dart +++ b/lib/src/iterable.dart @@ -19,8 +19,6 @@ import 'data_stractures/stack.dart'; import 'equality.dart'; extension CollectionsExtensions on Iterable { - - ///Sorts elements in the array in-place according to natural sort order of the value returned by specified [selector] function. Iterable sortBy( TKey Function(T) keySelector, { @@ -124,7 +122,7 @@ extension CollectionsExtensions on Iterable { } // Retuns map operation as a List - List mapList(E f(T e)) => this.map(f).toList(); + List mapList(E f(T e)) => this.map(f).toList(); // Takes the first half of a list List firstHalf() => take(halfLength).toList(); @@ -229,6 +227,18 @@ extension CollectionsExtensions on Iterable { 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 @@ -308,7 +318,7 @@ extension CollectionsExtensions on Iterable { return stack; } - bool get isEmptyOrNull => this == null || isEmpty; + bool get isEmptyOrNull => isEmpty; /// Zip is used to combine multiple iterables into a single list that contains /// the combination of them two. @@ -345,8 +355,7 @@ extension CollectionsExtensions on Iterable { } /// Creates a Map instance in which the keys and values are computed from the iterable. - Map associate(key(element), value(element)) => - Map.fromIterable(this, key: key, value: value); + Map 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. diff --git a/lib/src/strings.dart b/lib/src/string_ext.dart similarity index 91% rename from lib/src/strings.dart rename to lib/src/string_ext.dart index 46ef638..04ce965 100644 --- a/lib/src/strings.dart +++ b/lib/src/string_ext.dart @@ -1,4 +1,5 @@ -/* + + /* * Copyright 2020 Idan Ayalon. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +26,30 @@ class Message { } extension StringExtensions on String? { + + // Will add new line if the sentence is bigger the 2 words. + /// [afterWords] will add new line after the selected word + /// Example + /// 'Hi, my name is'.wrapString(2) + /// + /// will print: + /// Hi, my + /// name is + + String wrapString(int afterWords) { + + final wordsArr = this?.split(' ') ?? []; + + if (wordsArr.length > 2) { + final int middle = (this?.indexOf(wordsArr[afterWords]) ?? 0) - 1; + final prefix = this?.substring(0, middle); + final postfix = this?.substring(middle + 1); + return '$prefix\n$postfix'; + } + + return this ?? ''; + } + String generateMessageByGender({Gender? gender, Message? message}) => Intl.gender(gender.toString(), male: '$this ${message?.male}', female: '$this ${message?.female}', other: '$this ${message?.other}'); diff --git a/lib/src/utils.dart b/lib/src/utils.dart index fad3b9e..f66707b 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -12,6 +12,7 @@ */ void checkNullError(Object o) { + // ignore: unnecessary_null_comparison if (o == null) { throw NullIteratorError(); } diff --git a/pubspec.yaml b/pubspec.yaml index 44f084e..bfbcaf0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: dart_extensions description: Set of method-extensions for dart that makes using framework in a much easier and clean way also adds additional functionality. -version: 2.0.1 +version: 2.0.3 documentation: https://github.com/droididan/dart_extentions homepage: https://github.com/droididan/dart_extentions @@ -16,5 +16,5 @@ dependencies: sdk: flutter dev_dependencies: - test: ^1.16.7 + test: 1.17.1 \ No newline at end of file diff --git a/test/enum_test.dart b/test/enum_test.dart new file mode 100644 index 0000000..c72c118 --- /dev/null +++ b/test/enum_test.dart @@ -0,0 +1,14 @@ +import 'package:test/expect.dart'; +import 'package:test/test.dart'; +import '../lib/emum.dart'; + +enum TestEnum { testValue1, testValue2, testValue3 } +enum OtherEnumForTesting { helloImAnEnumValue } + +void main() { + test('it should convert enums to string', () { + expect(TestEnum.testValue1.convertToString(), 'testValue1'); + expect(TestEnum.testValue2.convertToString(), 'testValue2'); + expect(TestEnum.testValue3.convertToString(), 'testValue3'); + }); +} diff --git a/test/iterable_tests.dart b/test/iterable_tests.dart index 54e4eda..0972bec 100644 --- a/test/iterable_tests.dart +++ b/test/iterable_tests.dart @@ -18,8 +18,16 @@ main() { final users = [User(22, "Ronit"), User(23, "Ronit"), User(22, "Oded"), User(32, "Shimi")]; group('iterables', () { + + test('if all elements match the predicate', () { + final isEven = (x) => x % 2 == 0; + expect([5, 19, 2].all(isEven), isFalse); + expect([6, 12, 2].all(isEven), isTrue); + expect([].all(isEven), isTrue); + }); + test('mapList', () { - expect([3,4,5,6,7].mapList((item) => item.toDouble()), [3.0, 4.0, 5.0, 6.0, 7.0]); + expect([3, 4, 5, 6, 7].mapList((item) => item.toDouble()), [3.0, 4.0, 5.0, 6.0, 7.0]); }); test('any', () { @@ -28,8 +36,8 @@ main() { expect([].any((u) => u.name == 'Oded'), false); }); - test("first or null", (){ - final list= [1,45,6,9]; + test("first or null", () { + final list = [1, 45, 6, 9]; expect(null, list.firstWhere((element) => element == 7)); }); diff --git a/test/strings_test.dart b/test/strings_test.dart index 7386c58..06fef30 100644 --- a/test/strings_test.dart +++ b/test/strings_test.dart @@ -12,10 +12,15 @@ */ import 'package:test/test.dart'; -import 'package:dart_extensions/src/strings.dart'; +import 'package:dart_extensions/src/string_ext.dart'; main() { group('strings', () { + test('it should wrap a string', () { + expect('one two three four'.wrapString(2), 'one two\nthree four'); + expect('one two three four'.wrapString(3), 'one two three\nfour'); + }); + test('validateEmail', () { expect('name@domain.com'.validateEmail(), true); expect('name@domain'.validateEmail(), false); From 106871084fe01348d9ea38fdd2aac6f6b70b1674 Mon Sep 17 00:00:00 2001 From: Idan Ayalon Date: Fri, 23 Apr 2021 23:29:09 +0300 Subject: [PATCH 05/11] whereIndexed, all --- README.md | 2 + lib/src/iterable.dart | 43 ++++- test/iterable_tests.dart | 372 ++++++++++++++++++++------------------- 3 files changed, 229 insertions(+), 188 deletions(-) diff --git a/README.md b/README.md index 5a95fbd..25aa039 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ ## What New +* `Iterables` -> `all` -> Returns `true` if all elements match the given predicate. +* `Iterables` -> `whereIndexed` -> Will retrun new [Iterable] with all elements that satisfy the predicate * `Strings` -> `wrapString(int afterWords)` -> break the text in 2 lines (good to format titles and stuff like that) * `enum` -> convert enum item into text. * `SizedBox` extension - convert to percents πŸ’ͺ🏻 diff --git a/lib/src/iterable.dart b/lib/src/iterable.dart index 4953d95..e9c8121 100644 --- a/lib/src/iterable.dart +++ b/lib/src/iterable.dart @@ -18,6 +18,8 @@ import 'package:quiver/iterables.dart'; import 'data_stractures/stack.dart'; import 'equality.dart'; +typedef IndexedPredicate = bool Function(int index, T); + extension CollectionsExtensions on Iterable { ///Sorts elements in the array in-place according to natural sort order of the value returned by specified [selector] function. Iterable sortBy( @@ -168,6 +170,9 @@ extension CollectionsExtensions on Iterable { /// var name = [].firstOrDefault["jack"]; // jack T firstOrDefault(T defaultValue) => firstOrNull ?? defaultValue; + /// Will retrun new [Iterable] with all elements that satisfy the predicate [predicate], + Iterable whereIndexed(IndexedPredicate 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 @@ -228,13 +233,12 @@ extension CollectionsExtensions on Iterable { } /// Returns `true` if all elements match the given predicate. - /// Example: + /// 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; + if (!predicate!(e)) return false; } return true; } @@ -369,3 +373,36 @@ extension CollectionsExtensions on Iterable { return null; } } + +// A lazy [Iterable] skip elements do **NOT** match the predicate [_f]. +class _IndexedWhereIterable extends Iterable { + final Iterable _iterable; + final IndexedPredicate _f; + + _IndexedWhereIterable(this._iterable, this._f); + + @override + Iterator get iterator => _IndexedWhereIterator(_iterable.iterator, _f); +} + +/// [Iterator] for [_IndexedWhereIterable] +class _IndexedWhereIterator extends Iterator { + final Iterator _iterator; + final IndexedPredicate _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; +} diff --git a/test/iterable_tests.dart b/test/iterable_tests.dart index 0972bec..cdac2ca 100644 --- a/test/iterable_tests.dart +++ b/test/iterable_tests.dart @@ -17,190 +17,192 @@ import 'package:test/test.dart'; main() { final users = [User(22, "Ronit"), User(23, "Ronit"), User(22, "Oded"), User(32, "Shimi")]; - group('iterables', () { - - test('if all elements match the predicate', () { - final isEven = (x) => x % 2 == 0; - expect([5, 19, 2].all(isEven), isFalse); - expect([6, 12, 2].all(isEven), isTrue); - expect([].all(isEven), isTrue); - }); - - test('mapList', () { - expect([3, 4, 5, 6, 7].mapList((item) => item.toDouble()), [3.0, 4.0, 5.0, 6.0, 7.0]); - }); - - test('any', () { - expect(users.any((u) => u.name == 'Oded'), true); - expect(users.any((u) => u.name == 'Not Exists'), false); - expect([].any((u) => u.name == 'Oded'), false); - }); - - test("first or null", () { - final list = [1, 45, 6, 9]; - expect(null, list.firstWhere((element) => element == 7)); - }); - - test('filter', () { - [null, 1, 2].where((n) => n == 2); - final listWithNull = [null, User(1, "r"), User(2, "t")]; - final numberList = [1, 2, 3, 4, 5, 6]; - - expect(users.filter((u) => u.name == "Ronit"), [users[0], users[1]]); - expect(numberList.filter((n) => n > 4), [5, 6]); - expect(listWithNull.filter((u) => u?.name == "r"), [listWithNull[1]]); - }); - - test('filterNot', () { - final listWithNull = [null, User(1, "r"), User(2, "t")]; - final numberList = [1, 2, 3, 4, 5, 6]; - - expect(listWithNull.filterNot((u) => u?.name == "t"), [listWithNull[1]]); - expect(numberList.filterNot((n) => n > 4), [1, 2, 3, 4]); - }); - - test('takeOnly', () { - expect([1, 2, 3, 4].takeOnly(1), [1]); - expect([1, 2, 3, 4].takeOnly(2), [1, 2]); - expect([1, 2, 3, 4].takeOnly(10), []); - }); - - test('drop', () { - expect([1, 2, 3, 4].drop(1), [2, 3, 4]); - expect([1, 2, 3, 4].drop(2), [3, 4]); - expect([1, 2, 3, 4].drop(5), []); - }); - - test('firstHalf', () { - expect([1, 2, 3, 4].firstHalf(), [1, 2]); - }); - - test('secondHalf', () { - expect([1, 2, 3, 4].secondHalf(), [3, 4]); - }); - - test('swap', () { - expect([1, 2, 3, 4].swap(0, 1), [2, 1, 3, 4]); - }); - - test('lastOrDefault', () { - expect([1, 2, 3, 4].lastOrDefault(999), 4); - expect([].lastOrDefault(999), 999); - }); - - test('firstOrDefault', () { - expect([1, 2, 3, 4].firstOrDefault(999), 1); - expect([].firstOrDefault(999), 999); - }); - - test('sortedDescending', () { - expect([1, 2, 3, 4].sortedDescending(), [4, 3, 2, 1]); - }); - - test('containsAll', () { - expect([1, 2, 3, 4].containsAll([1, 2, 5]), false); - expect([1, 2, 3, 4].containsAll([1, 2, 4]), true); - expect([].containsAll([1, 2, 4]), false); - }); - - test('containsAll', () { - expect([1, 2, 3, 4].containsAll([1, 2, 5]), false); - expect([1, 2, 3, 4].containsAll([1, 2, 4]), true); - expect([].containsAll([1, 2, 4]), false); - }); - - test('containsAll', () { - expect(users.count((u) => u.age == 22), 2); - expect(users.count((u) => u.name == "Ronit"), 2); - expect(users.count((u) => u.name == "Bin"), 0); - }); - - test('distinctBy', () { - final expected = users.distinctBy((u) => u.age); - expect(users.distinctBy((u) => u.age), expected); - }); - - test('subtract', () { - expect([1, 2, 3, 4].subtract([3, 4]), [1, 2]); - expect([1, 2, 3, 4].subtract([1, 2, 3, 4]), []); - expect([1, 2, 3, 4].subtract([1, 2]), [3, 4]); - }); - - test('toStack', () { - var stack = [1, 2, 3, 4].toStack(); - expect(stack.pop(), 4); - expect(stack.top(), 3); - expect(stack.pop(), 3); - expect(stack.top(), 2); - }); - - test('concatWithSingleList', () { - expect([1, 2, 3, 4].concatWithSingleList([5, 6, 7, 8, 9, 10, 11]), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); - expect([1, 2, 3, 4].concatWithSingleList([]), []); - expect([].concatWithSingleList([5, 6, 7, 8, 9, 10, 11]), []); - }); - - test('concatWithMultipleList', () { - final listOfLists = [ - [5, 6, 7], - [8, 9, 10] - ]; - expect([1, 2, 3, 4].concatWithMultipleList(listOfLists), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); - expect([1, 2, 3, 4].concatWithMultipleList([[], []]), [1, 2, 3, 4]); - expect([].concatWithMultipleList(listOfLists), []); - }); - - test('zip', () { - final expectedResult = [ - [1, 2], - [3, 4], - [5, 6] - ]; - expect([1, 3, 5].zip([2, 4, 6]), expectedResult); - expect([1, 3, 5].zip([2, 4, 6, 7, 8]), expectedResult); - }); - - test('associate', () { - final users = [User(33, "Miki"), User(45, "Anna"), User(19, "Amit")]; - expect(users.associate((k) => k.name, (e) => e.age), {'Miki': 33, 'Anna': 45, 'Amit': 19}); - }); - - test('find', () { - final users = [User(22, "Ronit"), User(23, "Ronit"), User(22, "Oded"), User(32, "Shimi")]; - expect(users.find((u) => u.name == "Ronit"), users.first); - expect(users.find((u) => u.name == "Oded"), users[2]); - expect(users.find((u) => u.name == "Not Exists Name"), null); - expect(users.find((u) => u.age == 32), users.last); - expect(users.find((u) => u.age == 31), null); - }); - - test('gruopBy by age', () { - final expected = { - 22: [users[0], users[2]], - 23: [users[1]], - 32: [users[3]] - }; - - expect(users.groupBy((User u) => u.age), expected); - }); - - test('gourpBy by name', () { - final expected = { - 'Ronit': [users[0], users[1]], - 'Oded': [users[2]], - 'Shimi': [users[3]] - }; - expect(users.groupBy((User u) => u.name), expected); - }); - - test('toMutableSet', () { - expect([1, 1, 1, 1, 2, 3, 4].toMutableSet(), [1, 2, 3, 4]); - expect(["a", "b", "a"].toMutableSet(), ["a", "b"]); - }); - - test('intersect', () { - expect(Set.from([1, 2, 3, 4]).intersect(Set.from([3, 4, 5, 6])), [1, 2, 3, 4, 5, 6]); - expect(Set.from([-1, -2, -3, 4]).intersect(Set.from([3, 4, 5, 6])), [-1, -2, -3, 4, 3, 5, 6]); - }); + test('result of whereIndex', () { + final result = users.whereIndexed((index, _) => index == 2); + expect(result, [User(22, "Oded")]); + }); + + test('if all elements match the predicate', () { + final isEven = (x) => x % 2 == 0; + expect([5, 19, 2].all(isEven), isFalse); + expect([6, 12, 2].all(isEven), isTrue); + expect([].all(isEven), isTrue); + }); + + test('mapList', () { + expect([3, 4, 5, 6, 7].mapList((item) => item.toDouble()), [3.0, 4.0, 5.0, 6.0, 7.0]); + }); + + test('any', () { + expect(users.any((u) => u.name == 'Oded'), true); + expect(users.any((u) => u.name == 'Not Exists'), false); + expect([].any((u) => u.name == 'Oded'), false); + }); + + test("first or null", () { + final list = [1, 45, 6, 9]; + expect(null, list.firstWhere((element) => element == 7)); + }); + + test('filter', () { + [null, 1, 2].where((n) => n == 2); + final listWithNull = [null, User(1, "r"), User(2, "t")]; + final numberList = [1, 2, 3, 4, 5, 6]; + + expect(users.filter((u) => u.name == "Ronit"), [users[0], users[1]]); + expect(numberList.filter((n) => n > 4), [5, 6]); + expect(listWithNull.filter((u) => u?.name == "r"), [listWithNull[1]]); + }); + + test('filterNot', () { + final listWithNull = [null, User(1, "r"), User(2, "t")]; + final numberList = [1, 2, 3, 4, 5, 6]; + + expect(listWithNull.filterNot((u) => u?.name == "t"), [listWithNull[1]]); + expect(numberList.filterNot((n) => n > 4), [1, 2, 3, 4]); + }); + + test('takeOnly', () { + expect([1, 2, 3, 4].takeOnly(1), [1]); + expect([1, 2, 3, 4].takeOnly(2), [1, 2]); + expect([1, 2, 3, 4].takeOnly(10), []); + }); + + test('drop', () { + expect([1, 2, 3, 4].drop(1), [2, 3, 4]); + expect([1, 2, 3, 4].drop(2), [3, 4]); + expect([1, 2, 3, 4].drop(5), []); + }); + + test('firstHalf', () { + expect([1, 2, 3, 4].firstHalf(), [1, 2]); + }); + + test('secondHalf', () { + expect([1, 2, 3, 4].secondHalf(), [3, 4]); + }); + + test('swap', () { + expect([1, 2, 3, 4].swap(0, 1), [2, 1, 3, 4]); + }); + + test('lastOrDefault', () { + expect([1, 2, 3, 4].lastOrDefault(999), 4); + expect([].lastOrDefault(999), 999); + }); + + test('firstOrDefault', () { + expect([1, 2, 3, 4].firstOrDefault(999), 1); + expect([].firstOrDefault(999), 999); + }); + + test('sortedDescending', () { + expect([1, 2, 3, 4].sortedDescending(), [4, 3, 2, 1]); + }); + + test('containsAll', () { + expect([1, 2, 3, 4].containsAll([1, 2, 5]), false); + expect([1, 2, 3, 4].containsAll([1, 2, 4]), true); + expect([].containsAll([1, 2, 4]), false); + }); + + test('containsAll', () { + expect([1, 2, 3, 4].containsAll([1, 2, 5]), false); + expect([1, 2, 3, 4].containsAll([1, 2, 4]), true); + expect([].containsAll([1, 2, 4]), false); + }); + + test('containsAll', () { + expect(users.count((u) => u.age == 22), 2); + expect(users.count((u) => u.name == "Ronit"), 2); + expect(users.count((u) => u.name == "Bin"), 0); + }); + + test('distinctBy', () { + final expected = users.distinctBy((u) => u.age); + expect(users.distinctBy((u) => u.age), expected); + }); + + test('subtract', () { + expect([1, 2, 3, 4].subtract([3, 4]), [1, 2]); + expect([1, 2, 3, 4].subtract([1, 2, 3, 4]), []); + expect([1, 2, 3, 4].subtract([1, 2]), [3, 4]); + }); + + test('toStack', () { + var stack = [1, 2, 3, 4].toStack(); + expect(stack.pop(), 4); + expect(stack.top(), 3); + expect(stack.pop(), 3); + expect(stack.top(), 2); + }); + + test('concatWithSingleList', () { + expect([1, 2, 3, 4].concatWithSingleList([5, 6, 7, 8, 9, 10, 11]), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); + expect([1, 2, 3, 4].concatWithSingleList([]), []); + expect([].concatWithSingleList([5, 6, 7, 8, 9, 10, 11]), []); + }); + + test('concatWithMultipleList', () { + final listOfLists = [ + [5, 6, 7], + [8, 9, 10] + ]; + expect([1, 2, 3, 4].concatWithMultipleList(listOfLists), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + expect([1, 2, 3, 4].concatWithMultipleList([[], []]), [1, 2, 3, 4]); + expect([].concatWithMultipleList(listOfLists), []); + }); + + test('zip', () { + final expectedResult = [ + [1, 2], + [3, 4], + [5, 6] + ]; + expect([1, 3, 5].zip([2, 4, 6]), expectedResult); + expect([1, 3, 5].zip([2, 4, 6, 7, 8]), expectedResult); + }); + + test('associate', () { + final users = [User(33, "Miki"), User(45, "Anna"), User(19, "Amit")]; + expect(users.associate((k) => k.name, (e) => e.age), {'Miki': 33, 'Anna': 45, 'Amit': 19}); + }); + + test('find', () { + final users = [User(22, "Ronit"), User(23, "Ronit"), User(22, "Oded"), User(32, "Shimi")]; + expect(users.find((u) => u.name == "Ronit"), users.first); + expect(users.find((u) => u.name == "Oded"), users[2]); + expect(users.find((u) => u.name == "Not Exists Name"), null); + expect(users.find((u) => u.age == 32), users.last); + expect(users.find((u) => u.age == 31), null); + }); + + test('gruopBy by age', () { + final expected = { + 22: [users[0], users[2]], + 23: [users[1]], + 32: [users[3]] + }; + + expect(users.groupBy((User u) => u.age), expected); + }); + + test('gourpBy by name', () { + final expected = { + 'Ronit': [users[0], users[1]], + 'Oded': [users[2]], + 'Shimi': [users[3]] + }; + expect(users.groupBy((User u) => u.name), expected); + }); + + test('toMutableSet', () { + expect([1, 1, 1, 1, 2, 3, 4].toMutableSet(), [1, 2, 3, 4]); + expect(["a", "b", "a"].toMutableSet(), ["a", "b"]); + }); + + test('intersect', () { + expect(Set.from([1, 2, 3, 4]).intersect(Set.from([3, 4, 5, 6])), [1, 2, 3, 4, 5, 6]); + expect(Set.from([-1, -2, -3, 4]).intersect(Set.from([3, 4, 5, 6])), [-1, -2, -3, 4, 3, 5, 6]); }); } From be4a9e95453c8ba88270418941f6355deeb42d80 Mon Sep 17 00:00:00 2001 From: Idan Ayalon Date: Fri, 23 Apr 2021 23:32:02 +0300 Subject: [PATCH 06/11] badge commit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 25aa039..5131356 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -[![](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.3-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) ## What New From 4efcce90519d4c1ef9ed6ee3e9157e5c13f9fc84 Mon Sep 17 00:00:00 2001 From: Idan Ayalon Date: Fri, 23 Apr 2021 23:33:18 +0300 Subject: [PATCH 07/11] badge commit --- README.md | 2 +- pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5131356..611513e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -[![](https://img.shields.io/badge/ver-2.0.3-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.4-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) ## What New diff --git a/pubspec.yaml b/pubspec.yaml index bfbcaf0..afe733f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: dart_extensions description: Set of method-extensions for dart that makes using framework in a much easier and clean way also adds additional functionality. -version: 2.0.3 +version: 2.0.4 documentation: https://github.com/droididan/dart_extentions homepage: https://github.com/droididan/dart_extentions From 813ba3d7d472b1d3d5c73cdb463bfc284c2d3dc6 Mon Sep 17 00:00:00 2001 From: Idan Ayalon Date: Wed, 26 May 2021 12:14:58 +0300 Subject: [PATCH 08/11] version 2.0.5 --- CHANGELOG.md | 17 ++++ README.md | 30 +++--- lib/src/flutter/duration.dart | 14 +++ lib/src/iterable.dart | 74 ++++++++------- lib/src/string_ext.dart | 170 ++++++++++++++++++++++++++++++---- pubspec.yaml | 2 +- 6 files changed, 237 insertions(+), 70 deletions(-) create mode 100644 lib/src/flutter/duration.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index b4f12a3..a1e4bca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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` diff --git a/README.md b/README.md index 611513e..69b5606 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,23 @@ -[![](https://img.shields.io/badge/ver-2.0.4-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 -* `Iterables` -> `all` -> Returns `true` if all elements match the given predicate. -* `Iterables` -> `whereIndexed` -> Will retrun new [Iterable] with all elements that satisfy the predicate -* `Strings` -> `wrapString(int afterWords)` -> break the text in 2 lines (good to format titles and stuff like that) -* `enum` -> convert enum item into text. -* `SizedBox` extension - convert to percents πŸ’ͺ🏻 -* `Sound null-safety` added! πŸ’ͺ🏻 -* `BuildContext` extensions πŸ’ͺ🏻 -* `List` 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* diff --git a/lib/src/flutter/duration.dart b/lib/src/flutter/duration.dart new file mode 100644 index 0000000..4a59735 --- /dev/null +++ b/lib/src/flutter/duration.dart @@ -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); +} diff --git a/lib/src/iterable.dart b/lib/src/iterable.dart index db9a9a7..249b10b 100644 --- a/lib/src/iterable.dart +++ b/lib/src/iterable.dart @@ -21,6 +21,45 @@ import 'equality.dart'; typedef IndexedPredicate = bool Function(int index, T); extension CollectionsExtensions on Iterable { + /// Returns this Iterable if it's not `null` and the empty list otherwise. + Iterable orEmpty() => this; + + ///Returns `true` if this nullable iterable is either null or empty. + bool get isEmptyOrNull => this.isEmpty; + + /// 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 concatWithSingleList(Iterable iterable) { + if (isEmptyOrNull || iterable.isEmptyOrNull) return []; + + return [...this.orEmpty(), ...iterable]; + } + + /// Return a list concatenates the output of the current list and multiple [iterables] + List concatWithMultipleList(List> iterables) { + if (isEmptyOrNull || iterables.isEmptyOrNull) return []; + final list = iterables.toList(growable: false).expand((i) => i); + return [...this.orEmpty(), ...list]; + } + + /// Zip is used to combine multiple iterables into a single list that contains + /// the combination of them two. + zip(Iterable iterable) sync* { + if (iterable.isEmptyOrNull) return; + final iterables = List.empty()..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); + } + } + ///Sorts elements in the array in-place according to natural sort order of the value returned by specified [selector] function. Iterable sortBy( TKey Function(T) keySelector, { @@ -29,13 +68,6 @@ extension CollectionsExtensions on Iterable { 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 toMutableSet() => Set.from(this); @@ -322,20 +354,6 @@ extension CollectionsExtensions on Iterable { return stack; } - bool get isEmptyOrNull => isEmpty; - - /// Zip is used to combine multiple iterables into a single list that contains - /// the combination of them two. - zip(Iterable iterable) sync* { - if (iterable.isEmptyOrNull) return; - final iterables = List.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: @@ -344,20 +362,6 @@ extension CollectionsExtensions on Iterable { /// ([1, 2, 3], [4, 5, 6], [7, 8, 9], [10]) Iterable> chunks(int size) => partition(this, size); - /// Return a list concatenates the output of the current list and another [iterable] - List concatWithSingleList(Iterable iterable) { - if (isEmptyOrNull || iterable.isEmptyOrNull) return []; - - return [...this, ...iterable]; - } - - /// Return a list concatenates the output of the current list and multiple [iterables] - List concatWithMultipleList(List> iterables) { - if (isEmptyOrNull || iterables.isEmptyOrNull) return []; - final list = iterables.toList(growable: false).expand((i) => i); - return [...this, ...list]; - } - /// Creates a Map instance in which the keys and values are computed from the iterable. Map associate(key(element), value(element)) => Map.fromIterable(this, key: key, value: value); diff --git a/lib/src/string_ext.dart b/lib/src/string_ext.dart index 04ce965..85f81ed 100644 --- a/lib/src/string_ext.dart +++ b/lib/src/string_ext.dart @@ -1,5 +1,4 @@ - - /* +/* * Copyright 2020 Idan Ayalon. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,6 +11,7 @@ * limitations under the License. */ +import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; enum Gender { male, female } @@ -19,25 +19,151 @@ enum Gender { male, female } class Message { final String male, female, other; - Message({required this.male,required this.female,required this.other}) + Message({required this.male, required this.female, required this.other}) : assert(male.isEmptyOrNull), assert(female.isEmptyOrNull), assert(other.isEmptyOrNull); } extension StringExtensions on String? { + bool isNull(dynamic value) => value == null; + + /// Returns whether a dynamic value PROBABLY + /// has the isEmpty getter/method by checking + /// standard dart types that contains it. + /// + /// This is here to for the 'DRY' + bool? _isEmpty(dynamic value) { + if (value is String) { + return value.toString().trim().isEmpty; + } + if (value is Iterable || value is Map) { + return value.isEmpty as bool?; + } + return false; + } + + + + /// Checks if string is a valid username. + bool isUsername(String s) => hasMatch(s, r'^[a-zA-Z0-9][a-zA-Z0-9_.]+[a-zA-Z0-9]$'); + + /// Checks if string is Palindrom. + bool isPalindrom(String string) { + final cleanString = string.toLowerCase().replaceAll(RegExp(r"\s+"), '').replaceAll(RegExp(r"[^0-9a-zA-Z]+"), ""); + + for (var i = 0; i < cleanString.length; i++) { + if (cleanString[i] != cleanString[cleanString.length - i - 1]) { + return false; + } + } + + return true; + } + + /// Checks if string is Currency. + bool isCurrency(String s) => hasMatch(s, + r'^(S?\$|\β‚©|Rp|\Β₯|\€|\β‚Ή|\β‚½|fr|R\$|R)?[ ]?[-]?([0-9]{1,3}[,.]([0-9]{3}[,.])*[0-9]{3}|[0-9]+)([,.][0-9]{1,2})?( ?(USD?|AUD|NZD|CAD|CHF|GBP|CNY|EUR|JPY|IDR|MXN|NOK|KRW|TRY|INR|RUB|BRL|ZAR|SGD|MYR))?$'); + + /// Checks if string is phone number. + bool isPhoneNumber(String s) { + if (s.length > 16 || s.length < 9) return false; + return hasMatch(s, r'^[+]*[(]{0,1}[0-9]{1,4}[)]{0,1}[-\s\./0-9]*$'); + } + + /// Checks if string is email. + bool isEmail(String s) => hasMatch(s, + r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$'); + + /// Checks if string is an html file. + bool isHTML(String filePath) { + return filePath.toLowerCase().endsWith(".html"); + } + + /// Checks if string is an video file. + bool isVideo(String filePath) { + var ext = filePath.toLowerCase(); + + return ext.endsWith(".mp4") || + ext.endsWith(".avi") || + ext.endsWith(".wmv") || + ext.endsWith(".rmvb") || + ext.endsWith(".mpg") || + ext.endsWith(".mpeg") || + ext.endsWith(".3gp"); + } + + /// Checks if string is an audio file. + bool isAudio(String filePath) { + final ext = filePath.toLowerCase(); + + return ext.endsWith(".mp3") || + ext.endsWith(".wav") || + ext.endsWith(".wma") || + ext.endsWith(".amr") || + ext.endsWith(".ogg"); + } + + /// Checks if string is an image file. + bool isImage(String filePath) { + final ext = filePath.toLowerCase(); + + return ext.endsWith(".jpg") || + ext.endsWith(".jpeg") || + ext.endsWith(".png") || + ext.endsWith(".gif") || + ext.endsWith(".bmp"); + } + + bool hasMatch(String? value, String pattern) { + return (value == null) ? false : RegExp(pattern).hasMatch(value); + } + + // Check if the string has any number in it, not accepting double, so don't + // use "." + isNumericOnly(String s) => hasMatch(s, r'^\d+$'); + + /// Checks if string consist only Alphabet. (No Whitespace) + bool isAlphabetOnly(String s) => hasMatch(s, r'^[a-zA-Z]+$'); + + /// Checks if string contains at least one Capital Letter + bool hasCapitalletter(String s) => hasMatch(s, r'[A-Z]'); + + /// Checks if data is null or blank (empty or only contains whitespace). + bool? isBlank(dynamic value) { + return _isEmpty(value); + } + + /// Checks if string is boolean. + bool isBool(String value) { + if (isNull(value)) { + return false; + } - // Will add new line if the sentence is bigger the 2 words. + return (value == 'true' || value == 'false'); + } + + Size get getTextSize { + final TextPainter textPainter = TextPainter( + text: TextSpan(text: this), + maxLines: 1, + )..layout( + minWidth: 0, + maxWidth: double.infinity, + ); + return textPainter.size; + } + + // Will add new line if the sentence is bigger the 2 words. /// [afterWords] will add new line after the selected word /// Example /// 'Hi, my name is'.wrapString(2) - /// + /// /// will print: /// Hi, my /// name is - + String wrapString(int afterWords) { - final wordsArr = this?.split(' ') ?? []; if (wordsArr.length > 2) { @@ -54,7 +180,7 @@ extension StringExtensions on String? { male: '$this ${message?.male}', female: '$this ${message?.female}', other: '$this ${message?.other}'); bool validateEmail() { - if(this == null) return false; + if (this == null) return false; return RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+").hasMatch(this!); } @@ -63,7 +189,7 @@ extension StringExtensions on String? { /// Return the string only if the delimiter exists in both ends, otherwise it will return the current string String? removeSurrounding(String delimiter) { - if(this == null) return null; + if (this == null) return null; final prefix = delimiter; final suffix = delimiter; @@ -79,10 +205,12 @@ extension StringExtensions on String? { /// Replace part of string after the first occurrence of given delimiter with the [replacement] string. /// If the string does not contain the delimiter, returns [defaultValue] which defaults to the original string. String? replaceAfter(String delimiter, String replacement, [String? defaultValue]) { - if(this == null) return null; + if (this == null) return null; final index = this!.indexOf(delimiter); return (index == -1) - ? defaultValue.isEmptyOrNull ? this : defaultValue + ? defaultValue.isEmptyOrNull + ? this + : defaultValue : this!.replaceRange(index + 1, this!.length, replacement); } @@ -90,14 +218,18 @@ extension StringExtensions on String? { /// Replace part of string before the first occurrence of given delimiter with the [replacement] string. /// If the string does not contain the delimiter, returns [missingDelimiterValue?] which defaults to the original string. String? replaceBefore(String delimiter, String replacement, [String? defaultValue]) { - if(this == null) return null; + if (this == null) return null; final index = this!.indexOf(delimiter); - return (index == -1) ? defaultValue.isEmptyOrNull ? this : defaultValue : this!.replaceRange(0, index, replacement); + return (index == -1) + ? defaultValue.isEmptyOrNull + ? this + : defaultValue + : this!.replaceRange(0, index, replacement); } ///Returns `true` if at least one element matches the given [predicate]. /// the [predicate] should have only one character - bool anyChar(bool predicate(String element)) => this.isEmptyOrNull ? false: this!.split('').any((s) => predicate(s)); + bool anyChar(bool predicate(String element)) => this.isEmptyOrNull ? false : this!.split('').any((s) => predicate(s)); /// Returns the string if it is not `null`, or the empty string otherwise String get orEmpty => this ?? ""; @@ -117,7 +249,7 @@ extension StringExtensions on String? { } /// Parses the string as an double or returns `null` if it is not a number. - double? toDoubleOrNull() => this == null ? null :double.tryParse(this!); + double? toDoubleOrNull() => this == null ? null : double.tryParse(this!); /// Parses the string as an int or returns `null` if it is not a number. int? toIntOrNull() => this == null ? null : int.tryParse(this!); @@ -143,11 +275,9 @@ extension StringExtensions on String? { /// Shrink a string to be no more than [maxSize] in length, extending from the end. /// For example, in a string with 10 charachters, a [maxSize] of 3 would return the last 3 charachters. - String? limitFromEnd(int maxSize) => (this?.length ?? 0) < maxSize - ? this - : this!.substring(this!.length - maxSize); + String? limitFromEnd(int maxSize) => (this?.length ?? 0) < maxSize ? this : this!.substring(this!.length - maxSize); - /// Shrink a string to be no more than [maxSize] in length, extending from the start. + /// Shrink a string to be no more than [maxSize] in length, extending from the start. /// For example, in a string with 10 charachters, a [maxSize] of 3 would return the first 3 charachters. String? limitFromStart(int maxSize) => (this?.length ?? 0) < maxSize ? this : this!.substring(0, maxSize); @@ -155,7 +285,7 @@ extension StringExtensions on String? { /// /// Returns `true` if this string is any of these values: `"true"`, `"yes"`, `"1"`, or if the string is a number and greater than 0, `false` if less than 1. This is also case insensitive. bool get asBool { - var s = this?.trim().toLowerCase()??"false"; + var s = this?.trim().toLowerCase() ?? "false"; num n; try { n = num.parse(s); diff --git a/pubspec.yaml b/pubspec.yaml index afe733f..12f7ae7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: dart_extensions description: Set of method-extensions for dart that makes using framework in a much easier and clean way also adds additional functionality. -version: 2.0.4 +version: 2.0.5 documentation: https://github.com/droididan/dart_extentions homepage: https://github.com/droididan/dart_extentions From abe82fe7b3f1f1352331ed9113536a942622aa04 Mon Sep 17 00:00:00 2001 From: Vincenzo Sarnataro <> Date: Fri, 26 Mar 2021 15:39:56 +0100 Subject: [PATCH 09/11] add extensions for nullable Iterable add orEmpty extensions --- lib/src/iterable.dart | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/src/iterable.dart b/lib/src/iterable.dart index 249b10b..7c69efb 100644 --- a/lib/src/iterable.dart +++ b/lib/src/iterable.dart @@ -18,14 +18,13 @@ import 'package:quiver/iterables.dart'; import 'data_stractures/stack.dart'; import 'equality.dart'; -typedef IndexedPredicate = bool Function(int index, T); +extension CollectionsNullableExtension on Iterable? { -extension CollectionsExtensions on Iterable { - /// Returns this Iterable if it's not `null` and the empty list otherwise. - Iterable orEmpty() => this; + /// Returns this Iterable if it's not `null` and the empty list otherwise. + Iterable orEmpty() => this ?? Iterable.empty(); ///Returns `true` if this nullable iterable is either null or empty. - bool get isEmptyOrNull => this.isEmpty; + bool get isEmptyOrNull => this?.isEmpty ?? true; /// Returns `true` if at least one element matches the given [predicate]. bool any(bool predicate(T element)) { @@ -52,14 +51,20 @@ extension CollectionsExtensions on Iterable { /// the combination of them two. zip(Iterable iterable) sync* { if (iterable.isEmptyOrNull) return; - final iterables = List.empty()..add(this.orEmpty())..add(iterable); + final iterables = List.empty() + ..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 = bool Function(int index, T); + +extension CollectionsExtensions on Iterable { ///Sorts elements in the array in-place according to natural sort order of the value returned by specified [selector] function. Iterable sortBy( TKey Function(T) keySelector, { From 8cd7d19bf96eb71b09971665be88b08cc67ea566 Mon Sep 17 00:00:00 2001 From: Vincenzo Sarnataro <> Date: Fri, 26 Mar 2021 15:40:07 +0100 Subject: [PATCH 10/11] add new test --- test/iterable_tests.dart | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/iterable_tests.dart b/test/iterable_tests.dart index cdac2ca..18616cd 100644 --- a/test/iterable_tests.dart +++ b/test/iterable_tests.dart @@ -201,6 +201,26 @@ main() { expect(["a", "b", "a"].toMutableSet(), ["a", "b"]); }); + test('intersect', () { + expect(Set.from([1, 2, 3, 4]).intersect(Set.from([3, 4, 5, 6])), [1, 2, 3, 4, 5, 6]); + expect(Set.from([-1, -2, -3, 4]).intersect(Set.from([3, 4, 5, 6])), [-1, -2, -3, 4, 3, 5, 6]); + }); + + test('orEmpty',(){ + List? list; + expect(list.orEmpty(), List.empty()); + list= [1,2,3]; + expect(list.orEmpty(), [1,2,3]); + }); + + test('isEmptyOrNull',(){ + List? list; + expect(list.isEmptyOrNull, true); + list= []; + expect(list.isEmptyOrNull, true); + list= [1,2,3]; + expect(list.isEmptyOrNull, false); + }); test('intersect', () { expect(Set.from([1, 2, 3, 4]).intersect(Set.from([3, 4, 5, 6])), [1, 2, 3, 4, 5, 6]); expect(Set.from([-1, -2, -3, 4]).intersect(Set.from([3, 4, 5, 6])), [-1, -2, -3, 4, 3, 5, 6]); From d9c2443f1a219ff913f67688b9b14f53329b34ff Mon Sep 17 00:00:00 2001 From: Vincenzo Sarnatarp Date: Mon, 31 May 2021 22:20:41 +0200 Subject: [PATCH 11/11] fix test in iterable_tests.dart --- lib/src/iterable.dart | 6 +++--- lib/src/model/user.dart | 13 +++++++++++++ test/iterable_tests.dart | 7 +++++-- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/lib/src/iterable.dart b/lib/src/iterable.dart index 7c69efb..fba5aca 100644 --- a/lib/src/iterable.dart +++ b/lib/src/iterable.dart @@ -51,7 +51,7 @@ extension CollectionsNullableExtension on Iterable? { /// the combination of them two. zip(Iterable iterable) sync* { if (iterable.isEmptyOrNull) return; - final iterables = List.empty() + final iterables = List.empty(growable: true) ..add(this.orEmpty()) ..add(iterable); @@ -128,7 +128,7 @@ extension CollectionsExtensions on Iterable { List takeOnly(int n) { if (n == 0) return []; - var list = List.empty(); + var list = List.empty(growable: true); var thisList = this.toList(); if (this is Iterable) { final resultSize = this.length - n; @@ -146,7 +146,7 @@ extension CollectionsExtensions on Iterable { List drop(int n) { if (n == 0) return []; - var list = List.empty(); + var list = List.empty(growable: true); var originalList = this.toList(); if (this is Iterable) { final resultSize = this.length - n; diff --git a/lib/src/model/user.dart b/lib/src/model/user.dart index 57be87f..5df6f80 100644 --- a/lib/src/model/user.dart +++ b/lib/src/model/user.dart @@ -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"; diff --git a/test/iterable_tests.dart b/test/iterable_tests.dart index 18616cd..b6422ea 100644 --- a/test/iterable_tests.dart +++ b/test/iterable_tests.dart @@ -19,7 +19,7 @@ main() { test('result of whereIndex', () { final result = users.whereIndexed((index, _) => index == 2); - expect(result, [User(22, "Oded")]); + expect(result.toList(), [User(22, "Oded")]); }); test('if all elements match the predicate', () { @@ -41,7 +41,10 @@ main() { test("first or null", () { final list = [1, 45, 6, 9]; - expect(null, list.firstWhere((element) => element == 7)); + expect(1, list.firstOrNull); + final list2 =List.empty(); + expect(null, list2.firstOrNull); + }); test('filter', () {