*vital-data-list.txt* list utilities library. Maintainer: ujihisa ============================================================================== CONTENTS *Vital.Data.List-contents* INTRODUCTION |Vital.Data.List-introduction| INTERFACE |Vital.Data.List-interface| Functions |Vital.Data.List-functions| ============================================================================== INTRODUCTION *Vital.Data.List-introduction* *Vital.Data.List* is a list utilities library. It provides some functions to manipulate |List|. > let s:V = vital#of("vital") let s:L = s:V.import("Data.List") let s = [] echo s:L.push(s, 1) " [1] echo s:L.push(s, 2) " [1, 2] echo s:L.push(s, 3) " [1, 2, 3] echo s " [1, 2, 3] echo s:L.pop(s) " 3 echo s:L.pop(s) " 2 echo s " [1] < ============================================================================== INTERFACE *Vital.Data.List-interface* ------------------------------------------------------------------------------ FUNCTIONS *Vital.Data.List-functions* pop({list}) *Vital.Data.List.pop()* Removes the last element from |List| {list} and returns the element, as if the {list} is a stack. push({list}, {val}) *Vital.Data.List.push()* Appends {val} to the end of |List| {list} and returns the list itself, as if the {list} is a stack. shift({list}) *Vital.Data.List.shift()* Removes the first element from |List| {list} and returns the element. unshift({list}, {val}) *Vital.Data.List.unshift()* Inserts {val} to the head of |List| {list} and returns the list itself. cons({val}, {list}) *Vital.Data.List.cons()* Makes new |List| which first item is {val} and the rest of items are |List| {list}. See also: |Vital.Data.List.conj()| > echo s:L.cons(1, [2, 3]) " [1, 2, 3] echo s:L.cons(1, []) " [1] echo s:L.cons([1], [2, 3]) " [[1], 2, 3] echo s:L.cons([1], 2) " ERROR: E745 < conj({list}, {val}) *Vital.Data.List.conj()* Makes new |List| which first items are |List| {list} and the final item is {val}. See also: |Vital.Data.List.cons()| > echo s:L.conj([2, 3], 1) " [2, 3, 1] echo s:L.conj([], 1) " [1] echo s:L.conj([2, 3], [1]) " [2, 3, [1]] echo s:L.conj(2, [1]) " ERROR: E745 < uniq({list}) *Vital.Data.List.uniq()* Removes duplicate elements from |List| {list}, nondestructively. In particular, it keeps only the first occurrence of each element. See also: |Vital.Data.List.uniq_by()| > uniq(['vim', 'emacs', 'vim', 'vim']) == ['vim', 'emacs'] < uniq_by({list}, {str}) *Vital.Data.List.uniq_by()* Removes duplicate elements from |List| {list}, nondestructively. In particular, it keeps only the first occurrence of each element. The uniqueness is judged with the value {str} to which a formula is applied. See also: |Vital.Data.List.uniq()| > uniq_by( \ ['vim', 'Vim', 'VIM', 'emacs', 'Emacs', 'EMACS', 'gVim', 'GVIM'], \ 'tolower(v:val)') == ['vim', 'emacs', 'gVim'] < clear({list}) *Vital.Data.List.clear()* Removes all the items of |List| {list}. Returns the empty list. concat({list}) *Vital.Data.List.concat()* Concatenates |List| {list} of lists. > echo s:L.concat([[1], [2, 3]]) " [1, 2, 3] < This is similar to |Vital.Data.List.flatten()| but this doesn't flatten recursively. flatten({list} [, {limit}]) *Vital.Data.List.flatten()* Take each {list} elements in |List| {list} into a new {list} recursively. When the {limit} argument is given, the function keeps nested items by the {limit} is maximum size. > echo s:L.flatten([[1], [2, 3]]) " [1, 2, 3] echo s:L.flatten([[1], 2, 3]) " [1, 2, 3] echo s:L.flatten([[['a']], [[['b']], 'c']], 2) " ['a', ['b'], 'c'] < sort({list}, {expr}) *Vital.Data.List.sort()* Sorts the items in |List| {list} in-place. Returns {list}. {expr} must be a |String| or a |Funcref|. When {expr} is a |Funcref|, this function returns the same result as |sort()|. When {expr} is a |String|, this function uses {expr} to compare items. Inside {expr} a:a and a:b have the value of the current items. The evaluating result of {expr} must have zero if they are equal, 1 or bigger if a:a sorts after the a:b, -1 or smaller if a:a sorts before a:b. > function! MyCompare(i1, i2) return a:i1 == \ a:i2 ? 0 : \ a:i1 > a:i2 ? 1 : \ -1 endfunction let list = ['pineapple', 'orange', 'banana', 'apple'] echo s:L.sort(copy(list), function('MyCompare')) " ['apple', 'banana', 'orange', 'pineapple'] echo s:L.sort([3, 1, 2], 'a:a - a:b') " [1, 2, 3] echo s:L.sort(copy(list), 'len(a:a)-len(a:b)') " ['apple', 'orange', 'banana', 'pineapple'] < sort_by({list}, {expr}) *Vital.Data.List.sort_by()* Returns a sorted |List| with key in |List| {list}. max_by({list}, {expr}) *Vital.Data.List.max_by()* Returns a maximum value in {list} through given {expr}. Returns 0 if {list} is empty. "v:val" can be used in {expr}. > echo s:L.max_by(['pineapple','orange','banana','apple'], 'len(v:val)') " pineapple echo s:L.max_by([20, -50, -15, 30], 'abs(v:val)') " -50 < min_by({list}, {expr}) *Vital.Data.List.min_by()* Returns a minimum value in |List| {list} through given {expr}. Returns 0 if {list} is empty. "v:val" can be used in {expr}. > echo s:L.min_by(['pineapple','orange','banana','apple'], 'len(v:val)') " apple echo s:L.min_by([20, -50, -15, 30], 'abs(v:val)') " -15 < char_range({from}, {to}) *Vital.Data.List.char_range()* Returns a |List| of letters from {from} to {to}. has({list}, {value}) *Vital.Data.List.has()* Returns Number 1 if {value} is in |List| {list}, otherwise zero. has_index({list}, {index}) *Vital.Data.List.has_index()* Returns Number 1 if can point to {index} for |List| {list}, otherwise zero. If {index} is negative Number, this function returns zero. span({expr}, {list}) *Vital.Data.List.span()* Returns a list of two lists where concatenation of them is equal to {list}, all the items of the first list satisfy {expr} and the first item of the second list does not satisfy {expr}. Inside {expr} |v:val| has the value of the current item. > echo s:L.span("v:val==1", [1, 2]) " [[1], [2]] echo s:L.span('v:val < 5', [1, 3, 5, 2]) " [[1, 3], [5, 2]] echo s:L.span('v:val > 3', [1, 2, 3, 4, 5]) " [[], [1, 2, 3, 4, 5]] echo s:L.span('v:val < 3', [1, 2, 3, 4, 5]) " [[1, 2], [3, 4, 5]] < If you know Haskell, this span() is like Haskell's Data.List.span just for your info. break({expr}, {list}) *Vital.Data.List.break()* Returns a list of two lists where concatenation of them is equal to {list}, all the items of the first list do not satisfy {expr} and the first item of the second list satisfies {expr}. Inside {expr} |v:val| has the value of the current item. > echo s:L.break("v:val==1", [1, 2]) " [[], [1, 2]] echo s:L.break('v:val == 5', [1, 3, 5, 2]) " [[1, 3], [5, 2]] echo s:L.break('v:val > 3', [1, 2, 3, 4, 5]) " [[1, 2, 3], [4, 5]] echo s:L.break('v:val < 3', [1, 2, 3, 4, 5]) " [[], [1, 2, 3, 4, 5]] < If you know Haskell, this break() is like Haskell's Data.List.break just for your info. take_while({expr}, {list}) *Vital.Data.List.take_while()* Returns a list which is from the beginning of the given {list} to an element that all of them satisfies given expression {expr}. Inside {expr} |v:val| has the value of the current item. > echo s:L.take_while("v:val==1", [1, 2]) " [1] echo s:L.take_while('v:val < 5', [1, 3, 5, 2]) " [1, 3] echo s:L.take_while('v:val > 3', [1, 2, 3, 4, 5]) " [] echo s:L.take_while('v:val < 3', [1, 2, 3, 4, 5]) " [1, 2] < If you know Haskell, this take_while() is like Haskell's Data.List.takeWhile just for your info. all({expr}, {list}) *Vital.Data.List.all()* Returns Number 1 if all the items in |List| {list} fulfill the condition {expr}, zero otherwise. If {list} is empty, this function returns 1. > echo s:L.all('v:val % 2 == 0', [2, 8, 4, 6]) " 1 echo s:L.all('v:val % 2 == 1', [2, 8, 4, 6]) " 0 echo s:L.all('v:val % 2 == 0', [2, 8, 5, 6]) " 0 echo s:L.all('0 < v:val', [2, 8, 4, 6]) " 1 echo s:L.all('0 < v:val', [2, 0, 4, 6]) " 0 < If you know Haskell, this all() is like Haskell's Prelude.all just for your info. any({expr}, {list}) *Vital.Data.List.any()* Returns Number 1 if at least one item in |List| {list} fulfills the condition {expr}, zero otherwise. If {list} is empty, this function returns 0. > echo s:L.any('v:val % 2 == 0', [2, 8, 4, 6]) " 1 echo s:L.any('v:val % 2 == 1', [2, 8, 4, 6]) " 0 echo s:L.any('v:val % 2 == 0', [2, 8, 5, 6]) " 1 echo s:L.any('0 < v:val', [2, 8, 4, 6]) " 1 echo s:L.any('0 < v:val', [2, 0, 4, 6]) " 1 < If you know Haskell, this any() is like Haskell's Prelude.any just for your info. and({list}) *Vital.Data.List.and()* Returns Number 1 if all the items of |List| {list} are non-zero Numbers, zero otherwise. If {list} is empty, this function returns 1. > echo s:L.and([1, 2, 3, 1]) " 1 echo s:L.and([1, 0, 3, 1]) " 0 echo s:L.and([0, 0, 0, 0]) " 0 < If you know Haskell, this and() is like Haskell's Prelude.and just for your info. or({list}) *Vital.Data.List.or()* Returns Number 1 if at least one item in List {list} is non-zero, zero otherwise. If {list} is empty, this function returns 0. > echo s:L.or([1, 2, 3, 1]) " 1 echo s:L.or([1, 0, 3, 1]) " 1 echo s:L.or([0, 0, 0, 0]) " 0 < If you know Haskell, this or() is like Haskell's Prelude.or just for your info. partition({expr}, {list}) *Vital.Data.List.partition()* TODO Behaves like Haskell's Data.List.partition(). map_accum({expr}, {xs}, {init}) *Vital.Data.List.map_accum()* This is similar to |map()| but the followings are different: * it doesn't destroy {xs} * it holds previous accumulator * you also have to specify initial accumulator value * you also have to let {expr} return the next accumulator value TODO usage foldl({f}, {init}, {xs}) *Vital.Data.List.foldl()* Reduces the list {xs} using the binary operator {f}, from left to right. The starting value of the reduction (typically the left-identity of the operator) is {init}. foldl(f, z, [x1, x2, ..., xn]) == f(... f(f(z, x1), x2) ..., xn) Examples: > echo s:L.foldl('v:memo + v:val', 0, range(1, 10)) " 55 := 1+2+3+4+5+6+7+8+9+10 echo s:L.foldl('v:memo + v:val', 20, range(1, 10)) " 75 := 20 + 1+2+3+4+5+6+7+8+9+10 echo s:L.foldl('[v:memo, v:val]', [], [1, 2]) " [[[], 1], 2] < See also: foldl1, foldr, foldr1 If you know Haskell, this foldl() is like Haskell's Data.List.foldl just for your info. foldl1({f}, {xs}) *Vital.Data.List.foldl1()* TODO Behaves like Haskell's Data.List.foldl1(). > echo s:L.foldl1('v:memo + v:val', range(1, 10)) " 55 echo s:L.foldl1('[v:memo, v:val]', [1, 2]) " [1, 2] < foldr({f}, {init}, {xs}) *Vital.Data.List.foldr()* TODO Behaves like Haskell's Data.List.foldr(). > echo s:L.foldr('v:memo + v:val', 0, range(1, 10)) " 55 echo s:L.foldr('v:memo + v:val', 20, range(1, 10)) " 75 echo s:L.foldr('[v:memo, v:val]', [], [1, 2]) " [[[], 2], 1] < foldr1({f}, {xs}) *Vital.Data.List.foldr1()* TODO Behaves like Haskell's Data.List.foldr1(). > echo s:L.foldr1('v:memo + v:val', range(1, 10)) " 55 echo s:L.foldr1('[v:memo, v:val]', [1, 2]) " [1, 2] < zip(...) *Vital.Data.List.zip()* TODO Behaves like python's zip(). > echo s:L.zip([1, 2, 3], [4, 5, 6]) " [[1, 4], [2, 5], [3, 6]] echo s:L.zip([1, 2, 3], [4, 5, 6], [7, 8, 9]) " [[1, 4, 7], [2, 5, 8], [3, 6, 9]] < with_index({list} [, {offset}]) *Vital.Data.List.with_index()* Returns {list} with index. {offset} means the base of index. If you specify {offset}, index starts with {offset}. > echo s:L.with_index(['a', 'b', 'c']) " [['a', 0], ['b', 1], ['c', 2]] echo s:L.with_index(['a', 'b', 'c'], 2) " [['a', 2], ['b', 3], ['c', 4]] < This function is useful when used with |:for|. For example, when you have lines as a list of string and you want to output a line with a line number to each line, you may write as below. > for idx in range(1, len(lines)) echo idx.': '.lines[idx] endfor < This procedure can be rewritten using with_index() as below. > for [line, idx] in s:L.with_index(lines, 1) echo idx.': '.line endfor < find({list}, {default}, {f}) *Vital.Data.List.find()* Returns the first value in {list} where the given {expr} is satisfied. {default} is returned when no item satisfies {expr}. > echo s:L.find([1, 2, 3, 1, 2, 3], '*not-found*', 'v:val % 2 == 0') " 2 echo s:L.find([1, 2, 3], '*not-found*', 'v:val % 10 == 0') " '*not-found*' < If you know Haskell, this find() is like Haskell's Data.List.find just for your info. *Vital.Data.List.find_index()* find_index({list}, {expr} [, {start} [, {default}]]) Returns the lowest index in {list} where the given {expr} is satisfied. If you specify {start}, start looking at the item with index {start} (may be negative for an item relative to the end). {default} is returned when no item satisfies {expr}. If {default} is omitted, -1 is used. > echo s:L.find_index([0, 1, 2, 3], 'v:val%2 == 1') " 1 echo s:L.find_index([0, 1, 2, 3], 'v:val > 10') " -1 < *Vital.Data.List.find_last_index()* find_last_index({list}, {expr} [, {start} [, {default}]]) Similar to find_index but this returns the highest index. Traversing is done in reverse order. > echo s:L.find_last_index([0, 1, 2, 3], 'v:val%2 == 1') " 3 < *Vital.Data.List.find_indices()* find_indices({list}, {expr} [, {start}]) Similar to find_index but this returns all of indices specifying {expr}. When no indices found, empty list is returned. > echo s:L.find_indices([0, 1, 2, 3], 'v:val%2 == 1') " [1, 3] echo s:L.find_indices([0, 1, 2, 3], 'v:val > 10') " [] < has_common_items({list1}, {list2}) *Vital.Data.List.has_common_items()* Returns non-zero if a:list1 and a:list2 have a common item, otherwise zero. > echo s:L.has_common_items(['a', 'b', 'c'], ['b', 'c']) " 1 echo s:L.has_common_items(['a', 'c'], ['b', 'c']) " 1 echo s:L.has_common_items(['a'], ['b', 'c']) " 0 intersect({list1}, {list2}) *Vital.Data.List.intersect()* Returns a |List| of common items between {list1} and {list2}, and it's unordered and uniquified. > echo s:L.intersect(['a', 'b', 'c'], ['b', 'c']) " ['b', 'c'] echo s:L.intersect(['a', 'c'], ['b', 'c']) " ['c'] echo s:L.intersect(['a', 'a'], ['a', 'a']) " ['a'] echo s:L.intersect(['a'], ['b', 'c']) " [] < group_by({list}, {expr}) *Vital.Data.List.group_by()* Returns a |Dictionary| grouped by the result of {expr}. "v:val" can be used in {expr}. > echo s:L.group_by(['a', 'b', 'ab'], 'len(v:val)') " {'1': ['a', 'b'], '2': ['ab']} echo s:L.group_by(['a', 'b', 'ab'], 'v:val[0]') " {'a': ['a', 'ab'], 'b': ['b']} < *Vital.Data.List.binary_search()* binary_search({list}, {expr}, [{func}, [{dict}]]) Returns the index in {list} where the item has a value equal to {expr} by binary search. {list} must be sorted. If {expr} is not found, it returns -1. When {func} is given, it is used to check the lhs of {func} is less than the rhs of {func}. {func} is the same as |sort()| of {func}. You can reuse {func} used for |sort()| to search with |Vital.Data.List.binary_search|. {dict} is used as "self" in "dict" function. > echo s:L.binary_search([1, 3, 5, 7], 3) " 1 echo s:L.binary_search([1, 3, 5, 7], 2) " -1 function! CompareWithFirstElem(a, b) return a:a[0] < a:b[0] ? -1 : a:a[0] > a:b[0] ? 1 : 0 endfunction echo s:L.binary_search([[1, 'd'], [3, 'c'], [5, 'b'], [7, 'a']], [3, 'c'], 'CompareWithFirstElem') " 1 echo s:L.binary_search([[1, 'd'], [3, 'c'], [5, 'b'], [7, 'a']], [10, 'c'], 'CompareWithFirstElem') " -1 < You can control the condition for the search by {func}. Below example shows the way to search a list by its length. > let CompareByLength = {} function! CompareByLength.func(a, b) dict return len(a:a) - len(a:b) endfunction echo s:L.binary_search(['a', 'aa', 'aaa'], 'vi', CompareByLength.func, CompareByLength) " 1 echo s:L.binary_search(['a', 'aa', 'aaa'], 'vivi', CompareByLength.func, CompareByLength) " -1 < product({lists}) *Vital.Data.List.product()* Returns Cartesian product of elements in the {lists}. > echo s:L.product([[1, 2], [4, 5]]) " [[1, 4], [1, 5], [2, 4], [2, 5]] echo s:L.product([range(2), range(2), range(2)]) " [[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]] < permutations({list} [, {r}]) *Vital.Data.List.permutations()* Returns successive {r} length permutations of elements in the {list}. If {r} is not specified, then {r} defaults to the length of the {list} and all possible full-length permutations are generated. > echo s:L.permutations([1, 2, 3]) " [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]] echo s:L.permutations([1, 2, 3], 2) " [[1, 2] , [1, 3], [2, 1], [2, 3], [3, 1], [3, 2]] < combinations({list}, {r}) *Vital.Data.List.combinations()* Returns successive {r} length combinations of elements in the {list}. > echo s:L.combinations([1, 2, 3, 4], 2) " [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]] echo s:L.combinations([5, 2, 3, 1], 3) " [[5, 2, 3], [5, 2, 1], [5, 3, 1], [2, 3, 1]] < ============================================================================== vim:tw=78:fo=tcq2mM:ts=8:ft=help:norl