heart 0.5.8 copy "heart: ^0.5.8" to clipboard
heart: ^0.5.8 copied to clipboard

Extension methods for Strings and lists, inspired by Haskell

Heart #

Extension methods for strings and lists, inspired by Haskell.

#

Alphabetical list of features: after, any, ascending, average, backwards, before, chr, chrs, concat, count, deepContains, deepEquals, descending, drop, dropWhile, every, filter, group, groupBy, head, inclusive, inclusiveString, indices, inits, insertInOrder, intercalate, interleave, intersect, intersperse, isLowerCase, isUpperCase, last, letterCount, letters, nub, product, range, rangeString, removeWhitespace, replace, riffleIn, riffleOut, shuffled, splitAt, startsWith, subtract, subtractAll, sum, tail, tails, toStringList, union, unwords, wordCount, words, zip, zip2, zip3, zip4, >, >=, <, <=, ^, *

deepEquals, deepContains

This package uses Dart's DeepCollectionEquality from collection whenever posssible, so deepEquals([1, 2], [1, 2]) returns true, even though [1, 2] == [1, 2] is normally false. [{1: 2}, {2: 3}].deepContains({2: 3}) is also true.

#

(Strings are treated as lists in Haskell, and have many of the same functions.)

ascending, descending #

Sort lists and strings:

List<int> l = [4, 5, 1, 2, 3].ascending(); // [1, 2, 3, 4, 5]
List<int> l2 = [4, 5, 1, 2, 3].descending(); // [5, 4, 3, 2, 1]

String s = 'hello'.ascending(); // 'ehllo'
String s = 'hello'.descending(); // 'ollhe'

before, after #

Get everything before or after a sublist:

List<int>? l = [1, 2, 3, 3].before([3]); // [1, 2]
[1, 2, 3, 3].before([2, 3]) // [1]

[1, 2, 3, 3].after([2, 3]) // [3]

Optional skip parameter skips that many occurrences:

[1, 2, 3, 3].before([3], skip: 1) // [1, 2, 3]
[1, 2, 3, 3].after([3], skip: 1) // []

Returns null if doesn't contain sublist:

[1, 2, 3, 3].before([4, 5]) // null

startsWith #

Dart already has this for strings.

bool b = [1, 2, 3].startsWith([1, 2]); // true

nub #

Remove duplicates:

List<int> l = [1, 2, 1, 2].nub() // [1, 2]
String s = 'hello'.nub(); // 'helo'

Optional list or string parameter only looks at those elements:

[1, 1, 2, 2, 3, 3].nub([1, 2]) // [1, 2, 3, 3]
'aaabbbcc'.nub('ab') // 'abcc'

backwards #

Reverse a string or list:

List<int> l = [1, 2, 3].backwards(); // [3, 2, 1]
String s = 'hello'.backwards(); // 'olleh'

shuffled #

Returns a shuffled list or string. (Dart's shuffle method is void)

List<int> l = [1, 2, 3, 4, 5].shuffled();
// Specify a Random object:
List<int> l = [1, 2, 3, 4, 5].shuffled(Random.secure());

String s = 'hello'.shuffled();

sum, product, average #

Add or multiply numbers in a list:

int s = [1, 2, 3].sum(); // 6
int p = [4, 5, 6].product(); // 120
double a = [11, 2, 33, 55, 7, 2, 1].average(); // 15.857142857142858

// .average() works for Strings based on character codes
'abc'.average() // 'b'

count #

Count occurrences in a list or string:

int c = [1, 2, 1, 3].count(1); // 2
// Note: '.indices([1]).length' gives same result, and can be used for sublists instead of one element

[{1,2}, [1,3]].count({1,2}) // 1

'hello world'.count('l') // 3
'hello world'.count('ll') // 1

indices #

Find where sublist occurs in a list, or substring occurs in a string:

List<int> l = [1, 2, 1, 2, 1].indices([1]); // [0, 2, 4]
[1, 2, 1, 2, 1].indices([1, 2]) // [0, 2]

'hello'.indices('ll') // [2]

[[1,2], [1,2], {3: 4}].indices([[1, 2], {3: 4}]) // [1]

Optional exclusive parameter means the sublists at the result indices would be mutually exclusive:

[1, 1, 1, 1].indices([1, 1]) // [0, 1, 2]
[1, 1, 1, 1].indices([1, 1], exclusive: true) // [0, 2]
'aaaa'.indices('aa', exclusive: true) // [0, 2]

concat #

Concatenate nested lists or strings:

List<int> l = [[1, 2], [3, 4], [5, 6]].concat(); // [1, 2, 3, 4, 5, 6]
String str = ['hello', 'world'].concat(); // 'helloworld'

intersperse #

Inserts an item in between all other elements:

List<int> l = [1, 2, 3].intersperse(0); // [1, 0, 2, 0, 3]
String s = 'hello'.intersperse('-'); // 'h-e-l-l-o'
// Note: 'replace' method with empty list or string adds element to beginning and end
'hello'.replace('', '-') // '-h-e-l-l-o-'

intercalate (in-TER-kuh-late) #

Inserts a list between lists (or string between strings) and concatenates the result:

List<int> l = [[1, 2], [3, 4], [5, 6]].intercalate([0, 0]);
// [1, 2, 0, 0, 3, 4, 0, 0, 5, 6]

String s = ['hello', 'world'].intercalate('-');
// 'hello-world'

Optional count parameter only adds that many times:

[[1, 2], [3, 4], [5, 6]].intercalate([0, 0], count: 1) // [1, 2, 0, 0, 3, 4, 5, 6]

filter #

Keep only elements that meet criteria:

// Keep where x^3 < 10:
List<int> l = [1, 2, 3, 4].filter((x) => pow(x, 3) < 10); // [1, 2]

Equivalent to .where().toList(), but also works on Strings:

// '<' operator defined in this package
String s = 'hello world'.filter((char) => char < 'j'); // 'he d'

any, every #

(These already exist for lists)

bool b = 'hello'.any((char) => char == 'h'); // true
bool b2 = 'hello'.every((char) => char == 'h'); // false

drop, dropWhile #

drop(n) removes first n elements. Similar to .sublist(n) or .substring(n) but doesn't throw exception for invalid n.

List<int> l = [0, 1, 2].drop(1); // [1, 2]

// Returns the same if n<=0
[0, 1, 2].drop(-1) // [0, 1, 2]
// Returns empty if n >= length
[0, 1, 2].drop(100) // []

'hello'.drop(2)
// 'llo'

dropWhile drops elements until they don't meet criteria, keeps everything after.

List<int> l = [1, 2, 3, 2, 1].dropWhile((x) => x < 3);
// [3, 2, 1]

// '<' operator defined in this package
String s = 'hello'.dropWhile((char) => char < 'i');
// 'llo'

replace #

Remove all occurrences or replace with a particular sublist:

List<int> l = [1, 1, 2, 3].replace([1]); // [2, 3]
[1, 1, 2, 3].replace([1, 1], [99]) // [99, 2, 3]

'aaaa'.replace('a', 'b') // 'bbbb'

Optional third parameter only replaces that many occurrences:

[1, 1, 1, 1].replace([1], [3, 4], 2) // [3, 4, 3, 4, 1, 1]
'aaaa'.replace('a', 'bc', 2) // 'bcbcaa'

subtract, subtractAll #

subtract removes elements one at a time (like Haskell's \\):

List<int> l = [1, 1, 2, 2, 3].subtract([1, 3]); // [1, 2, 2]

l = [1, 1, 2, 2].subtract([1, 2, 3]); // [1, 2]
// ignores 3 since it is not in original list

String s = 'hello'.subtract('eo'); // 'hll'

subtractAll removes all occurrences:

List<int> l = [1, 1, 2, 2].subtractAll([1]); // [2, 2]
String s = 'hello'.subtractAll('lo'); // 'he'

union, intersect #

union adds elements that aren't already present.

It doesn't remove duplicates from original, but doesn't add duplicates from input.

List<int> l = [1, 1, 2, 3].union([2, 3, 4, 4]); // [1, 1, 2, 3, 4]
String s = 'hello'.union(' world'); // 'hello wrd'

(Use .nub() to remove duplicates, and concatenate normally to keep duplicates.)

intersect keeps all elements from original list that are also in input.

List<int> l = [1, 1, 2, 3].intersect([1, 2]); // [1, 1, 2]
String s = 'hello'.intersect('world'); // 'llo'
// Remove duplicates with .nub()

head, tail(s), last, inits, #

head returns first element.

tail returns everything but the first element.

last returns the last element (Dart has this for lists but not strings).

int? i = [1, 2, 3].head(); // 1
List<int>? l = [1, 2, 3].tail(); // [2, 3]
[1].tail() // []
[].tail() // null

'hello'.head() // 'h'
'hello'.tail() // 'ello'
'hello'.last() //'o'

inits returns a list of lists (or strings) by adding elements from the beginning:

[1, 2, 3].inits()
// [[], [1], [1, 2], [1, 2, 3]]

'hi'.inits()
// ['', 'h', 'hi']

tails returns a list of lists (or strings) by removing one element at a time from the beginning:

[1, 2, 3].tails()
// [[1, 2, 3], [2, 3], [3], []]

// inclusive function defined in this package
List<List<int>> twelveDaysOfChristmas = inclusive(12, 1).tails().backwards();
// [[], [1], [2, 1], [3, 2, 1], [4, 3, 2, 1], [5, 4, 3, 2, 1], [6, 5, 4, 3, 2, 1], [7, 6, 5, 4, 3, 2, 1], [8, 7, 6, 5, 4, 3, 2, 1], [9, 8, 7, 6, 5, 4, 3, 2, 1], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1], [12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]]

'hello'.tails()
// ['hello', 'ello', 'llo', 'lo', 'o', '']

insertInOrder #

Inserts a value before the first element that is >=. Does not sort.

List<double> l2 = [1.1, 2.2, 0.2].insertInOrder(1.7);
// [1.1, 1.7, 2.2, 0.2]

String s = 'ABDKEO'.insertInOrder('J'); // 'ABDJKEO'

splitAt #

Split a list or string into two:

List<List<int>> l = [5, 6, 7, 8].splitAt(2); // [[5, 6], [7, 8]]

'hello'.splitAt(2) // ['he', 'llo'] 

interleave #

Combine two lists or strings by taking turns:

List<int> l = [1, 2, 3].interleave([4, 5, 6]);
// [1, 4, 2, 5, 3, 6]

'abc'.interleave('123')
// 'a1b2c3'

Extra characters get added to the end:

[1, 2, 3, 4].interleave([5])
// [1, 5, 2, 3, 4]

riffleIn, riffleOut #

Riffle shuffle: splits list or string in half and interleaves them

image

// .riffleOut interleaves first half to second.
List<int> l = [1, 2, 3, 4, 5, 6].riffleOut();
// [1, 4, 2, 5, 3, 6]

// .riffleIn interleaves second half to first
List<int> l2 = [1, 2, 3, 4, 5, 6].riffleIn();
// [4, 1, 5, 2, 6, 3]

String s = '12345'.riffleOut();
// '14253'
String s2 = '12345'.riffleIn();
// '31425'

group, groupBy #

group combines consecutive elements together if they are equal:

List<List<int>> l = [1, 2, 3, 3, 1].group();
// [[1], [2], [3, 3], [1]]

List<String> ls = 'hello'.group();
// ['h', 'e', 'll', 'o']

groupBy combines consecutive elements if they meet criteria. In this example, items are in the same sublist if they are less than the one after:

List<List<int>> l = [1, 2, 3, 2, 1].groupBy((a, b) => a < b);
// [[1, 2, 3], [2], [1]]
List<String> ls = 'HelLo'.groupBy((a, b) => a.isUpperCase() && b.isLowerCase());
// ['He', 'l', 'Lo']

chr, chrs #

chr returns a String from a character code.

chrs returns a String from a list of codes.

97.chr() // 'a'
[97, 98].chrs() // 'ab'

// .codeUnits converts back to codes

Other methods for lists #

toStringList #

Convert all elements to strings:

List<String> l = [1, 2, 3].toStringList(); // ['1', '2', '3']

zip, zip2 #

zip takes in a list of lists, returns a list of lists where corresponding elements are paired together.

List l = zip([['one','two','three'], [1,2,3]]);
// [['one', 1], ['two', 2], ['three', 3]]

zip2 takes in a list of 2 lists and performs a function between corresponding elements (similar to Haskell's zipWith):

List l = zip2([[1,2,3],[4,5,6]], (a,b) => a+b); // [5, 7, 9]

zip3 and zip4 work similarly.

Other methods for Strings #

removeWhitespace #

String s = '  hello \n world  '.removeWhitespace(); // 'helloworld'

// Dart's .trim() only removes leading and trailing whitespace.

words, wordCount, letters, letterCount #

words returns a list of words without whitespace.

wordCount takes the length of this List. Equivalent to words().length.

List<String> listOfWords = 'hello world'.words(); // ['hello', 'world']
int w = 'hello world'.wordCount(); // 2

letters returns a List of all the characters, with optional keepWhitespace parameter.

letterCount counts all characters, with optional keepWhitespace parameter.

List<String> listOfCharacters = 'hello world'.letters();
// ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd']

int lc = 'hello world'.letterCount();
// 10
'hello world'.letters(keepWhitespace: true)
// ['h', 'e', 'l', 'l', ' ', 'o', 'w', 'o', 'r', 'l', 'd']

'hello world'.letterCount(keepWhitespace: true)
// 11 (same as .length)

unwords #

Combine a list of strings into one, with spaces in between:

String s = ['hello', 'world'].unwords(); // 'hello world'
String s2 = 'hello world'.letters().unwords(); 'h e l l o w o r l d'

isUpperCase, isLowerCase #

Checks if all characters are upper or lower case, with optional ignoreSymbols parameter.

bool b = 'hello world'.isLowerCase(); // true
bool b2 = 'hello world'.isLowerCase(ignoreSymbols: false); // false (because of space)

bool b3 = 'Hello'.isUpperCase(); // false
bool b4 = 'Hello'.isLowerCase(); // false

bool b5 = 'á'.isLowerCase(ignoreSymbols: false); // true
// accented letters don't count as symbols

Other features #

range, inclusive #

Generates a list of integers:

// "for(int i in range(5))" is the same as "for(int i = 0; i < 5; i++)"

List<int> l = range(5); // [0, 1, 2, 3, 4]
inclusive(5) // [0, 1, 2, 3, 4, 5]

range(-5) // [-4, -3, -2, -1, 0]
inclusive(-5) // [-5, -4, -3, -2, -1, 0]
range(0) // []
inclusive(0) // [0]

With two arguments, inclusive includes the second one, range does not .

range(1, 5) // [1, 2, 3, 4]
inclusive(1, 5) // [1, 2, 3, 4, 5]
range(1, -2) // [1, 0, -1]
inclusive(1, -2) // [1, 0, -1, -2]

Third argument adds a step count:

range(1, 5, 2) // [1, 3]
inclusive(1, 5, 2) // [1, 3, 5]
range(1, -5, -2) // [1, -1, -3]
inclusive(1, -5, -2) // [1, -1, -3, -5]

rangeString, inclusiveString #

Similar to range and inclusive. Strings must have exactly one character:

rangeString('a', 'f') // 'abcde'
rangeString('c', 'a') // 'cb'
rangeString('a', 'g', 2) // 'ace'

inclusiveString('a', 'c') // 'abc'
inclusiveString('c', 'a') // 'cba'
inclusiveString('a', 'g', 2) // 'aceg'

Operators for strings and lists #

>, >=, <, <= #

Compare elements in two lists, starting at the beginning:

[1, 2, 3] > [1, 1, 3] // true

Compare strings according to their character codes:

'b' > 'a' // true
'hello' < 'hi' // true

['a', 1] >= ['b', 1] // false

(If elements cannot be compared, both >= and <= will return false.)

^ #

Get next String by character codes:

String s = 'a' ^ 1; // 'b'
'b' ^ (-1) // 'a'
'abc' ^ 1 // 'bcd

* #

Repeat elements of a list with *

List<int> l = [1, 2] * 3; // [1, 2, 1, 2, 1, 2]

// Dart has this for Strings
String s = 'hello' * 3; // 'hellohellohello'
12
likes
160
points
59
downloads

Publisher

unverified uploader

Weekly Downloads

Extension methods for Strings and lists, inspired by Haskell

Repository (GitHub)

Documentation

API reference

License

MIT (license)

Dependencies

collection

More

Packages that depend on heart