From 575f2a56103d14f9277c57b6cd028186a33ff99a Mon Sep 17 00:00:00 2001 From: warrenrhodes Date: Mon, 10 Jun 2024 12:39:26 +0100 Subject: [PATCH] add custom function --- CHANGELOG.md | 6 ++ README.md | 10 +- example/dart_advanced_utils_example.dart | 13 +++ lib/src/custom_func.dart | 34 +++++++ lib/src/list_extensions.dart | 15 +++ lib/src/string_extensions.dart | 124 +++++++++++++++++++++++ pubspec.yaml | 2 +- test/custom_func_test.dart | 58 +++++++++++ test/list_extensions_test.dart | 6 +- 9 files changed, 265 insertions(+), 3 deletions(-) create mode 100644 lib/src/custom_func.dart create mode 100644 test/custom_func_test.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c64584..b214b1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.1.0 + +- Adds custom print function `fprint`. +- Adds `prod` function on integer list. +- Adds `swapcase`, `format` and `isDigit` on string extenxion. + ## 1.0.1 - Add `DateTimeExtensions` extension diff --git a/README.md b/README.md index 5665a73..ec31ecb 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Add the following dependency to your `pubspec.yaml` file: ```yaml dependencies: - dart_advanced_utils: ^1.0.0 + dart_advanced_utils: ^1.1.0 ``` Run `pub get` to install the package. @@ -35,6 +35,7 @@ import 'package:dart_advanced_utils/dart_advanced_utils.dart'; - **median**: Calculates the median of all elements in the list. - **min**: Find the minimum value in the list. - **max**: Find the maximum value in the list. +- **prod**: Returns the product of all elements in the list. ### String Extensions @@ -55,6 +56,9 @@ import 'package:dart_advanced_utils/dart_advanced_utils.dart'; - **strip**: Returns a new string with leading and trailing characters removed. - **title**: Returns a new string with the first letter of each word capitalized and all other letters lowercased. - **charToUpper**: Returns a new string with the character at the specified [index] capitalized. +- **format**: Formats the string by replacing placeholders with provided arguments. +- **isDigit**: Returns `true` if the string contains only digits, and `false` otherwise +- **swapcase**: Returns a new string with the case of each character swapped. ### Date Extensions @@ -73,6 +77,10 @@ import 'package:dart_advanced_utils/dart_advanced_utils.dart'; - **endOfMonth**Returns the last day of the month for a given date. - **hasALeapYear** Determines if the year of a given date is a leap year. +### Custom Function + +- **fprint** A formatted print function. + ### Examples ```dart diff --git a/example/dart_advanced_utils_example.dart b/example/dart_advanced_utils_example.dart index 43e600b..374a9a4 100644 --- a/example/dart_advanced_utils_example.dart +++ b/example/dart_advanced_utils_example.dart @@ -1,4 +1,5 @@ import 'package:dart_advanced_utils/dart_advanced_utils.dart'; +import 'package:dart_advanced_utils/src/custom_func.dart'; void main() { List numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; @@ -20,4 +21,16 @@ void main() { String? blankString = " "; print(blankString.isBlank); // Output: true + + print(numbers.prod); // Output: 3628800 + + String str = 'Hello {0}, my name is {1}'; + String str2 = 'Hello {}, my name is {}'; + print(str.format( + posArgs: ['Dart', 'John'])); // Output: Hello Dart, my name is John. + print(str2.format( + posArgs: ['Jhon', 'Smith'])); //Output: Hello Jhon, my name is Smith. + + final iterable = ['value1', 'value2']; + fprint(iterable, unpack: true); // Output: value1 value2 } diff --git a/lib/src/custom_func.dart b/lib/src/custom_func.dart new file mode 100644 index 0000000..2592a6c --- /dev/null +++ b/lib/src/custom_func.dart @@ -0,0 +1,34 @@ +/// A formatted print function. +/// +/// The function takes a list of objects to print, a separator to use between +/// the objects, an end character to append at the end of the output, and an +/// optional file to write the output to. +/// +/// Args: +/// args (Object?): The list of objects to print. +/// sep (String?): The separator to use between objects. Defaults to a space. +/// end (String): The end character to append at the end. Defaults to a newline. +/// unpack (bool): Whether to unpack the map or iterable objects. Defaults to false. +/// +/// Example: +/// fprint(['Hello', 'world', 123], sep: ', ', end: '!\n'); +/// fprint(['Dart', 'is', 'fun']); +void fprint(Object? args, + {String sep = ' ', String end = '\n', bool unpack = false}) { + String output; + if (args is Map) { + if (unpack) { + output = args.keys.join(sep); + } else { + output = args.toString(); + } + } else if (args is Iterable) { + output = unpack ? args.join(sep) : args.toString(); + } else { + output = args.toString(); + } + + output += end; + + print(output); +} diff --git a/lib/src/list_extensions.dart b/lib/src/list_extensions.dart index 6ae8a44..534fe6a 100644 --- a/lib/src/list_extensions.dart +++ b/lib/src/list_extensions.dart @@ -140,4 +140,19 @@ extension IntList on Iterable { T get min => length == 0 ? throw StateError('No element') : reduce((curr, next) => curr < next ? curr : next); + + /// Returns the product of all elements in the list. + /// + /// If the list is empty, a [StateError] is thrown with the message 'No element'. + /// + /// Returns the product of all elements in the list. + /// + /// Example: + /// ```dart + /// List numbers = [1, 2, 3]; + /// print(numbers.prod); // Output: 6 + /// + num get prod => length == 0 + ? throw StateError('No element') + : fold(1, (current, next) => current * next); } diff --git a/lib/src/string_extensions.dart b/lib/src/string_extensions.dart index d607ae0..1daacad 100644 --- a/lib/src/string_extensions.dart +++ b/lib/src/string_extensions.dart @@ -1,3 +1,29 @@ +extension NullStringExtensions on String? { + /// Returns `true` if the string is `null` or empty, `false` otherwise. + /// + /// This getter checks if the string is `null` or if it is empty. It returns `true` if the string is `null` or if it has no characters. + /// + /// Example: + /// ```dart + /// final String? myString = null; + /// final bool isNullOrEmpty = myString.isNullOrEmpty; + /// print(isNullOrEmpty); // Prints true + /// + /// final String emptyString = ''; + /// final bool isNullOrEmpty2 = emptyString.isNullOrEmpty; + /// print(isNullOrEmpty2); // Prints true + /// + /// final String nonEmptyString = 'Hello, world!'; + /// final bool isNullOrEmpty3 = nonEmptyString.isNullOrEmpty; + /// print(isNullOrEmpty3); // Prints false + /// ``` + /// + /// Returns: + /// - `true` if the string is `null` or empty. + /// - `false` otherwise. + bool get isNullOrEmpty => this == null || this!.isEmpty; +} + /// Extensions for formatting strings. /// /// This extension provides methods for formatting strings. @@ -366,4 +392,102 @@ extension StringFormat on String { int get wordCount { return trim().split(RegExp(r'\s+')).length; } + + /// Returns a new string with the case of each character swapped. + /// + /// If the string is empty, it returns the original string. + /// + /// Example: + /// ```dart + /// String str = "Hello world"; + /// print(str.swapcase); // Output: hELLO wORLD + /// ``` + String get swapcase { + if (isEmpty) return this; + return split('').map((char) { + if (char == char.toUpperCase()) return char.toLowerCase(); + return char.toUpperCase(); + }).join(''); + } + + /// Returns `true` if the string contains only digits, and `false` otherwise. + /// + /// This getter uses a regular expression to check if the string consists of one or more digits. + /// + /// Example: + /// ```dart + /// String str1 = "123"; + /// print(str1.isDigit); // Output: true + /// + /// String str2 = "abc"; + /// print(str2.isDigit); // Output: false + /// ``` + bool get isDigit => RegExp(r'^[0-9]+$').hasMatch(this); + + /// Formats the string by replacing placeholders with provided arguments. + /// + /// The function takes an optional list of positional arguments [posArgs] and + /// an optional map of named arguments [namedArgs]. The placeholders in the + /// string can be specified using curly braces `{}` for positional arguments + /// and `${name}` for named arguments. The function replaces these placeholders + /// with the corresponding values from the arguments. + /// + /// If [sep] is provided, it is used to join the words in the formatted string. + /// + /// Returns the formatted string. + /// + /// Example: + /// ```dart + /// String str = 'Hello {0}, my name is {1}'; + /// String str2 = 'Hello {}, my name is {}'; + /// print(str.format(posArgs: ['Dart', 'John'])); Output: Hello Dart, my name is John. + /// print(str2.format(posArgs: ['Jhon', 'Smith'])); Output: Hello Jhon, my name is Smith. + /// ``` + String format( + {List? posArgs, Map? namedArgs, String? sep}) { + String result = this; + + if (posArgs != null) { + for (int i = 0; i < posArgs.length; i++) { + result = result + .replaceAll('{$i}', posArgs[i].toString()) + .replaceFirst('{}', posArgs[i].toString()); + } + } + + if (namedArgs != null) { + namedArgs.forEach((key, value) { + result = result.replaceAll('{$key}', value.toString()); + }); + } + + return result.split(' ').join(sep ?? ' '); + } + + /// Returns `true` if the string is a valid URL, `false` otherwise. + /// + /// This getter checks if the string matches the regular expression pattern for a valid URL. + /// It returns `true` if the string is a valid URL, and `false` otherwise. + /// + /// Example: + /// ```dart + /// final String url1 = 'https://www.example.com'; + /// final bool isValidUrl1 = url1.isValidUrl; + /// print(isValidUrl1); // Prints true + /// + /// final String url2 = 'ftp://example.com'; + /// final bool isValidUrl2 = url2.isValidUrl; + /// print(isValidUrl2); // Prints true + /// + /// final String url3 = 'example.com'; + /// final bool isValidUrl3 = url3.isValidUrl; + /// print(isValidUrl3); // Prints false + /// ``` + /// + /// Returns: + /// - `true` if the string is a valid URL. + /// - `false` otherwise. + bool get isUrl => RegExp( + r"^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$") + .hasMatch(this); } diff --git a/pubspec.yaml b/pubspec.yaml index 3b0c4eb..5443502 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: dart_advanced_utils description: A Dart advanced utils package enhances the default dart object by adding powerful and flexible methods. -version: 1.0.1 +version: 1.1.0 repository: https://github.com/warrenrhodes/dart_utils issue_tracker: https://github.com/warrenrhodes/dart_utils/issues diff --git a/test/custom_func_test.dart b/test/custom_func_test.dart new file mode 100644 index 0000000..8e40299 --- /dev/null +++ b/test/custom_func_test.dart @@ -0,0 +1,58 @@ +import 'package:dart_advanced_utils/src/custom_func.dart'; +import 'package:test/test.dart'; + +void main() { + group('fprint', () { + test('should print a map with unpacked keys', () { + final map = {'key1': 'value1', 'key2': 'value2'}; + final expectedOutput = 'key1 key2'; + + expect(() => fprint(map, unpack: true), prints('$expectedOutput\n\n')); + }); + + test('should print a map with string representation', () { + final map = {'key1': 'value1', 'key2': 'value2'}; + final expectedOutput = '{key1: value1, key2: value2}'; + + expect(() => fprint(map), prints('$expectedOutput\n\n')); + }); + + test('should print an iterable with unpacked elements', () { + final iterable = ['value1', 'value2']; + final expectedOutput = 'value1 value2'; + + output() => fprint(iterable, unpack: true); + + expect(output, prints('$expectedOutput\n\n')); + }); + + test( + 'should print an iterable with unpacked elements and string representation', + () { + final iterable = ['value1', 'value2']; + final expectedOutput = 'value1-value2'; + + output() => fprint(iterable, unpack: true, sep: '-'); + + expect(output, prints('$expectedOutput\n\n')); + }); + + test('should print an iterable with string representation', () { + final iterable = ['value1', 'value2']; + final expectedOutput = '[value1, value2]'; + + output() => fprint(iterable); + + expect(output, prints('$expectedOutput\n\n')); + }); + + test('should print a single value', () { + final value = 'value'; + final expectedOutput = 'value'; + + output() => fprint(value); + + expect(output, prints('$expectedOutput\n\n')); + }); + }); +} diff --git a/test/list_extensions_test.dart b/test/list_extensions_test.dart index 90e3f9e..8174ea6 100644 --- a/test/list_extensions_test.dart +++ b/test/list_extensions_test.dart @@ -7,7 +7,6 @@ void main() { List> emptyList = [[]]; List> repeatedList = emptyList * 3; List repeatedList2 = [54] * 3; - expect(repeatedList, [[], [], []]); expect(repeatedList2, [54, 54, 54]); }); @@ -79,6 +78,11 @@ void main() { expect(numbers.min, equals(1)); }); + test('prod of non-empty iterable', () { + var numbers = [1, 2, 3, 4, 5]; + expect(numbers.prod, equals(120)); + }); + test('empty iterable throws StateError', () { List numbers = []; expect(() => numbers.sum, throwsA(isA()));