On this page
sequtils
Although this module has seq
in its name, it implements operations not only for seq type, but for three built-in container types under the openArray
umbrella:
- sequences
- strings
- array
The system module defines several common functions, such as:
newSeq[T]
for creating new sequences of typeT
@
for converting arrays and strings to sequencesadd
for adding new elements to strings and sequences&
for string and seq concatenationin
(alias forcontains
) andnotin
for checking if an item is in a container
This module builds upon that, providing additional functionality in form of procs, iterators and templates inspired by functional programming languages.
For functional style programming you have different options at your disposal:
- sugar.collect macro
- pass anonymous proc
- import sugar module and use => macro
- use ...It templates (mapIt, filterIt, etc.)
The chaining of functions is possible thanks to the method call syntax.
import sequtils, sugar
# Creating a sequence from 1 to 10, multiplying each member by 2,
# keeping only the members which are not divisible by 6.
let
foo = toSeq(1..10).map(x => x*2).filter(x => x mod 6 != 0)
bar = toSeq(1..10).mapIt(it*2).filterIt(it mod 6 != 0)
baz = collect(newSeq):
for i in 1..10:
let j = 2*i
if j mod 6 != 0:
j
doAssert foo == bar
doAssert foo == baz
echo foo # @[2, 4, 8, 10, 14, 16, 20]
echo foo.any(x => x > 17) # true
echo bar.allIt(it < 20) # false
echo foo.foldl(a + b) # 74; sum of all members
import sequtils
from strutils import join
let
vowels = @"aeiou" # creates a sequence @['a', 'e', 'i', 'o', 'u']
foo = "sequtils is an awesome module"
echo foo.filterIt(it notin vowels).join # "sqtls s n wsm mdl"
See also:
- strutils module for common string functions
- sugar module for syntactic sugar macros
- algorithm module for common generic algorithms
- json module for a structure which allows heterogeneous members
Imports
Procs
-
proc concat[T](seqs: varargs[seq[T]]): seq[T]
-
Takes several sequences' items and returns them inside a new sequence. All sequences must be of the same type.
See also:
- distribute proc for a reverse operation
Example:
Source Editlet s1 = @[1, 2, 3] s2 = @[4, 5] s3 = @[6, 7] total = concat(s1, s2, s3) assert total == @[1, 2, 3, 4, 5, 6, 7]
-
proc count[T](s: openArray[T]; x: T): int
-
Returns the number of occurrences of the item
x
in the containers
.Example:
Source Editlet a = @[1, 2, 2, 3, 2, 4, 2] b = "abracadabra" assert count(a, 2) == 4 assert count(a, 99) == 0 assert count(b, 'r') == 2
-
proc cycle[T](s: openArray[T]; n: Natural): seq[T]
-
Returns a new sequence with the items of the container
s
repeatedn
times.n
must be a non-negative number (zero or more).Example:
Source Editlet s = @[1, 2, 3] total = s.cycle(3) assert total == @[1, 2, 3, 1, 2, 3, 1, 2, 3]
-
proc repeat[T](x: T; n: Natural): seq[T]
-
Returns a new sequence with the item
x
repeatedn
times.n
must be a non-negative number (zero or more).Example:
Source Editlet total = repeat(5, 3) assert total == @[5, 5, 5]
-
proc deduplicate[T](s: openArray[T]; isSorted: bool = false): seq[T]
-
Returns a new sequence without duplicates.
Setting the optional argument
isSorted
totrue
(default: false) uses a faster algorithm for deduplication.Example:
Source Editlet dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4] dup2 = @["a", "a", "c", "d", "d"] unique1 = deduplicate(dup1) unique2 = deduplicate(dup2, isSorted = true) assert unique1 == @[1, 3, 4, 2, 8] assert unique2 == @["a", "c", "d"]
-
proc minIndex[T](s: openArray[T]): int
-
Returns the index of the minimum value of
s
.T
needs to have a<
operator.Example:
Source Editlet a = @[1, 2, 3, 4] b = @[6, 5, 4, 3] c = [2, -7, 8, -5] d = "ziggy" assert minIndex(a) == 0 assert minIndex(b) == 3 assert minIndex(c) == 1 assert minIndex(d) == 2
-
proc maxIndex[T](s: openArray[T]): int
-
Returns the index of the maximum value of
s
.T
needs to have a<
operator.Example:
Source Editlet a = @[1, 2, 3, 4] b = @[6, 5, 4, 3] c = [2, -7, 8, -5] d = "ziggy" assert maxIndex(a) == 3 assert maxIndex(b) == 0 assert maxIndex(c) == 2 assert maxIndex(d) == 0
-
proc zip[S, T](s1: openArray[S]; s2: openArray[T]): seq[(S, T)]
-
Returns a new sequence with a combination of the two input containers.
The input containers can be of different types. If one container is shorter, the remaining items in the longer container are discarded.
Note: For Nim 1.0.x and older version,
zip
returned a seq of named tuple with fieldsa
andb
. For Nim versions 1.1.x and newer,zip
returns a seq of unnamed tuples.Example:
Source Editlet short = @[1, 2, 3] long = @[6, 5, 4, 3, 2, 1] words = @["one", "two", "three"] letters = "abcd" zip1 = zip(short, long) zip2 = zip(short, words) assert zip1 == @[(1, 6), (2, 5), (3, 4)] assert zip2 == @[(1, "one"), (2, "two"), (3, "three")] assert zip1[2][0] == 3 assert zip2[1][1] == "two" when (NimMajor, NimMinor) <= (1, 0): let zip3 = zip(long, letters) assert zip3 == @[(a: 6, b: 'a'), (5, 'b'), (4, 'c'), (3, 'd')] assert zip3[0].b == 'a' else: let zip3: seq[tuple[num: int, letter: char]] = zip(long, letters) assert zip3 == @[(6, 'a'), (5, 'b'), (4, 'c'), (3, 'd')] assert zip3[0].letter == 'a'
-
proc unzip[S, T](s: openArray[(S, T)]): (seq[S], seq[T])
-
Returns a tuple of two sequences split out from a sequence of 2-field tuples.
Example:
Source Editlet zipped = @[(1, 'a'), (2, 'b'), (3, 'c')] unzipped1 = @[1, 2, 3] unzipped2 = @['a', 'b', 'c'] assert zipped.unzip() == (unzipped1, unzipped2) assert zip(unzipped1, unzipped2).unzip() == (unzipped1, unzipped2)
-
proc distribute[T](s: seq[T]; num: Positive; spread = true): seq[seq[T]]
-
Splits and distributes a sequence
s
intonum
sub-sequences.Returns a sequence of
num
sequences. For some input values this is the inverse of the concat proc. The input sequences
can be empty, which will producenum
empty sequences.If
spread
is false and the length ofs
is not a multiple ofnum
, the proc will max out the first sub-sequence with1 + len(s) div num
entries, leaving the remainder of elements to the last sequence.On the other hand, if
spread
is true, the proc will distribute evenly the remainder of the division across all sequences, which makes the result more suited to multithreading where you are passing equal sized work units to a thread pool and want to maximize core usage.Example:
Source Editlet numbers = @[1, 2, 3, 4, 5, 6, 7] assert numbers.distribute(3) == @[@[1, 2, 3], @[4, 5], @[6, 7]] assert numbers.distribute(3, false) == @[@[1, 2, 3], @[4, 5, 6], @[7]] assert numbers.distribute(6)[0] == @[1, 2] assert numbers.distribute(6)[1] == @[3]
-
proc map[T, S](s: openArray[T]; op: proc (x: T): S {...}{.closure.}): seq[S] {...}{.inline.}
-
Returns a new sequence with the results of
op
proc applied to every item in the containers
.Since the input is not modified you can use it to transform the type of the elements in the input container.
Instead of using
map
andfilter
, consider using thecollect
macro from thesugar
module.See also:
- sugar.collect macro
- mapIt template
- apply proc for the in-place version
Example:
Source Editlet a = @[1, 2, 3, 4] b = map(a, proc(x: int): string = $x) assert b == @["1", "2", "3", "4"]
-
proc apply[T](s: var openArray[T]; op: proc (x: var T) {...}{.closure.}) {...}{.inline.}
-
Applies
op
to every item ins
modifying it directly.Note that container
s
must be declared as avar
and it is required for your input and output types to be the same, sinces
is modified in-place. The parameter function takes avar T
type parameter.See also:
Example:
Source Editvar a = @["1", "2", "3", "4"] apply(a, proc(x: var string) = x &= "42") assert a == @["142", "242", "342", "442"]
-
proc apply[T](s: var openArray[T]; op: proc (x: T): T {...}{.closure.}) {...}{.inline.}
-
Applies
op
to every item ins
modifying it directly.Note that container
s
must be declared as avar
and it is required for your input and output types to be the same, sinces
is modified in-place. The parameter function takes and returns aT
type variable.See also:
Example:
Source Editvar a = @["1", "2", "3", "4"] apply(a, proc(x: string): string = x & "42") assert a == @["142", "242", "342", "442"]
-
proc apply[T](s: openArray[T]; op: proc (x: T) {...}{.closure.}) {...}{.inline.}
-
Same as
apply
but for proc that do not return and do not mutates
directly.Example:
Source Editapply([0, 1, 2, 3, 4], proc(item: int) = echo item)
-
proc filter[T](s: openArray[T]; pred: proc (x: T): bool {...}{.closure.}): seq[T] {...}{. inline.}
-
Returns a new sequence with all the items of
s
that fulfilled the predicatepred
(function that returns abool
).Instead of using
map
andfilter
, consider using thecollect
macro from thesugar
module.See also:
- sugar.collect macro
- filterIt template
- filter iterator
- keepIf proc for the in-place version
Example:
Source Editlet colors = @["red", "yellow", "black"] f1 = filter(colors, proc(x: string): bool = x.len < 6) f2 = filter(colors, proc(x: string): bool = x.contains('y')) assert f1 == @["red", "black"] assert f2 == @["yellow"]
-
proc keepIf[T](s: var seq[T]; pred: proc (x: T): bool {...}{.closure.}) {...}{.inline.}
-
Keeps the items in the passed sequence
s
if they fulfilled the predicatepred
(function that returns abool
).Note that
s
must be declared as avar
.Similar to the filter proc, but modifies the sequence directly.
See also:
Example:
Source Editvar floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1] keepIf(floats, proc(x: float): bool = x > 10) assert floats == @[13.0, 12.5, 10.1]
-
proc delete[T](s: var seq[T]; first, last: Natural)
-
Deletes in the items of a sequence
s
at positionsfirst..last
(including both ends of a range). This modifiess
itself, it does not return a copy.Example:
Source Editlet outcome = @[1, 1, 1, 1, 1, 1, 1, 1] var dest = @[1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1] dest.delete(3, 8) assert outcome == dest
-
proc insert[T](dest: var seq[T]; src: openArray[T]; pos = 0)
-
Inserts items from
src
intodest
at positionpos
. This modifiesdest
itself, it does not return a copy.Notice that
src
anddest
must be of the same type.Example:
Source Editvar dest = @[1, 1, 1, 1, 1, 1, 1, 1] let src = @[2, 2, 2, 2, 2, 2] outcome = @[1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1] dest.insert(src, 3) assert dest == outcome
-
proc all[T](s: openArray[T]; pred: proc (x: T): bool {...}{.closure.}): bool
-
Iterates through a container and checks if every item fulfills the predicate.
See also:
Example:
Source Editlet numbers = @[1, 4, 5, 8, 9, 7, 4] assert all(numbers, proc (x: int): bool = return x < 10) == true assert all(numbers, proc (x: int): bool = return x < 9) == false
-
proc any[T](s: openArray[T]; pred: proc (x: T): bool {...}{.closure.}): bool
-
Iterates through a container and checks if some item fulfills the predicate.
See also:
Example:
Source Editlet numbers = @[1, 4, 5, 8, 9, 7, 4] assert any(numbers, proc (x: int): bool = return x > 8) == true assert any(numbers, proc (x: int): bool = return x > 9) == false
Iterators
-
iterator filter[T](s: openArray[T]; pred: proc (x: T): bool {...}{.closure.}): T
-
Iterates through a container
s
and yields every item that fulfills the predicatepred
(function that returns abool
).Instead of using
map
andfilter
, consider using thecollect
macro from thesugar
module.See also:
Example:
Source Editlet numbers = @[1, 4, 5, 8, 9, 7, 4] var evens = newSeq[int]() for n in filter(numbers, proc (x: int): bool = x mod 2 == 0): evens.add(n) assert evens == @[4, 8, 4]
-
iterator items[T](xs: iterator (): T): T
- iterates over each element yielded by a closure iterator. This may not seem particularly useful on its own, but this allows closure iterators to be used by the the mapIt, filterIt, allIt, anyIt, etc. templates. Source Edit
Macros
-
macro mapLiterals(constructor, op: untyped; nested = true): untyped
-
Applies
op
to each of the atomic literals like3
or"abc"
in the specifiedconstructor
AST. This can be used to map every array element to some target type:Example:
let x = mapLiterals([0.1, 1.2, 2.3, 3.4], int) doAssert x is array[4, int]
Short notation for:
let x = [int(0.1), int(1.2), int(2.3), int(3.4)]
If
nested
is true (which is the default), the literals are replaced everywhere in theconstructor
AST, otherwise only the first level is considered:let a = mapLiterals((1.2, (2.3, 3.4), 4.8), int) let b = mapLiterals((1.2, (2.3, 3.4), 4.8), int, nested=false) assert a == (1, (2, 3), 4) assert b == (1, (2.3, 3.4), 4) let c = mapLiterals((1, (2, 3), 4, (5, 6)), `$`) let d = mapLiterals((1, (2, 3), 4, (5, 6)), `$`, nested=false) assert c == ("1", ("2", "3"), "4", ("5", "6")) assert d == ("1", (2, 3), "4", (5, 6))
There are no constraints for the
Source Editconstructor
AST, it works for nested tuples of arrays of sets etc.
Templates
-
template filterIt(s, pred: untyped): untyped
-
Returns a new sequence with all the items of
s
that fulfilled the predicatepred
.Unlike the filter proc and filter iterator, the predicate needs to be an expression using the
it
variable for testing, like:filterIt("abcxyz", it == 'x')
.Instead of using
mapIt
andfilterIt
, consider using thecollect
macro from thesugar
module.See also:
Example:
Source Editlet temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44] acceptable = temperatures.filterIt(it < 50 and it > -10) notAcceptable = temperatures.filterIt(it > 50 or it < -10) assert acceptable == @[-2.0, 24.5, 44.31] assert notAcceptable == @[-272.15, 99.9, -113.44]
-
template keepItIf(varSeq: seq; pred: untyped)
-
Keeps the items in the passed sequence (must be declared as a
var
) if they fulfilled the predicate.Unlike the keepIf proc, the predicate needs to be an expression using the
it
variable for testing, like:keepItIf("abcxyz", it == 'x')
.See also:
Example:
Source Editvar candidates = @["foo", "bar", "baz", "foobar"] candidates.keepItIf(it.len == 3 and it[0] == 'b') assert candidates == @["bar", "baz"]
-
template countIt(s, pred: untyped): int
-
Returns a count of all the items that fulfilled the predicate.
The predicate needs to be an expression using the
it
variable for testing, like:countIt(@[1, 2, 3], it > 2)
.Example:
Source Editlet numbers = @[-3, -2, -1, 0, 1, 2, 3, 4, 5, 6] iterator iota(n: int): int = for i in 0..<n: yield i assert numbers.countIt(it < 0) == 3 assert countIt(iota(10), it < 2) == 2
-
template allIt(s, pred: untyped): bool
-
Iterates through a container and checks if every item fulfills the predicate.
Unlike the all proc, the predicate needs to be an expression using the
it
variable for testing, like:allIt("abba", it == 'a')
.See also:
Example:
Source Editlet numbers = @[1, 4, 5, 8, 9, 7, 4] assert numbers.allIt(it < 10) == true assert numbers.allIt(it < 9) == false
-
template anyIt(s, pred: untyped): bool
-
Iterates through a container and checks if some item fulfills the predicate.
Unlike the any proc, the predicate needs to be an expression using the
it
variable for testing, like:anyIt("abba", it == 'a')
.See also:
Example:
Source Editlet numbers = @[1, 4, 5, 8, 9, 7, 4] assert numbers.anyIt(it > 8) == true assert numbers.anyIt(it > 9) == false
-
template toSeq(iter: untyped): untyped
-
Transforms any iterable (anything that can be iterated over, e.g. with a for-loop) into a sequence.
Example:
Source Editlet myRange = 1..5 mySet: set[int8] = {5'i8, 3, 1} assert typeof(myRange) is HSlice[system.int, system.int] assert typeof(mySet) is set[int8] let mySeq1 = toSeq(myRange) mySeq2 = toSeq(mySet) assert mySeq1 == @[1, 2, 3, 4, 5] assert mySeq2 == @[1'i8, 3, 5]
-
template foldl(sequence, operation: untyped): untyped
-
Template to fold a sequence from left to right, returning the accumulation.
The sequence is required to have at least a single element. Debug versions of your program will assert in this situation but release versions will happily go ahead. If the sequence has a single element it will be returned without applying
operation
.The
operation
parameter should be an expression which uses the variablesa
andb
for each step of the fold. Since this is a left fold, for non associative binary operations like subtraction think that the sequence of numbers 1, 2 and 3 will be parenthesized as (((1) - 2) - 3).See also:
- foldl template with a starting parameter
- foldr template
Example:
Source Editlet numbers = @[5, 9, 11] addition = foldl(numbers, a + b) subtraction = foldl(numbers, a - b) multiplication = foldl(numbers, a * b) words = @["nim", "is", "cool"] concatenation = foldl(words, a & b) procs = @["proc", "Is", "Also", "Fine"] proc foo(acc, cur: string): string = result = acc & cur assert addition == 25, "Addition is (((5)+9)+11)" assert subtraction == -15, "Subtraction is (((5)-9)-11)" assert multiplication == 495, "Multiplication is (((5)*9)*11)" assert concatenation == "nimiscool" assert foldl(procs, foo(a, b)) == "procIsAlsoFine"
-
template foldl(sequence, operation, first): untyped
-
Template to fold a sequence from left to right, returning the accumulation.
This version of
foldl
gets a starting parameter. This makes it possible to accumulate the sequence into a different type than the sequence elements.The
operation
parameter should be an expression which uses the variablesa
andb
for each step of the fold. Thefirst
parameter is the start value (the firsta
) and therefor defines the type of the result.See also:
Example:
Source Editlet numbers = @[0, 8, 1, 5] digits = foldl(numbers, a & (chr(b + ord('0'))), "") assert digits == "0815"
-
template foldr(sequence, operation: untyped): untyped
-
Template to fold a sequence from right to left, returning the accumulation.
The sequence is required to have at least a single element. Debug versions of your program will assert in this situation but release versions will happily go ahead. If the sequence has a single element it will be returned without applying
operation
.The
operation
parameter should be an expression which uses the variablesa
andb
for each step of the fold. Since this is a right fold, for non associative binary operations like subtraction think that the sequence of numbers 1, 2 and 3 will be parenthesized as (1 - (2 - (3))).See also:
- foldl template
- foldl template with a starting parameter
Example:
Source Editlet numbers = @[5, 9, 11] addition = foldr(numbers, a + b) subtraction = foldr(numbers, a - b) multiplication = foldr(numbers, a * b) words = @["nim", "is", "cool"] concatenation = foldr(words, a & b) assert addition == 25, "Addition is (5+(9+(11)))" assert subtraction == 7, "Subtraction is (5-(9-(11)))" assert multiplication == 495, "Multiplication is (5*(9*(11)))" assert concatenation == "nimiscool"
-
template mapIt(s: typed; op: untyped): untyped
-
Returns a new sequence with the results of
op
proc applied to every item in the containers
.Since the input is not modified you can use it to transform the type of the elements in the input container.
The template injects the
it
variable which you can use directly in an expression.Instead of using
mapIt
andfilterIt
, consider using thecollect
macro from thesugar
module.See also:
- sugar.collect macro
- map proc
- applyIt template for the in-place version
Example:
Source Editlet nums = @[1, 2, 3, 4] strings = nums.mapIt($(4 * it)) assert strings == @["4", "8", "12", "16"]
-
template applyIt(varSeq, op: untyped)
-
Convenience template around the mutable
apply
proc to reduce typing.The template injects the
it
variable which you can use directly in an expression. The expression has to return the same type as the sequence you are mutating.See also:
Example:
Source Editvar nums = @[1, 2, 3, 4] nums.applyIt(it * 3) assert nums[0] + nums[3] == 15
-
template newSeqWith(len: int; init: untyped): untyped
-
Creates a new sequence of length
len
, callinginit
to initialize each value of the sequence.Useful for creating "2D" sequences - sequences containing other sequences or to populate fields of the created sequence.
Example:
Source Edit## Creates a sequence containing 5 bool sequences, each of length of 3. var seq2D = newSeqWith(5, newSeq[bool](3)) assert seq2D.len == 5 assert seq2D[0].len == 3 assert seq2D[4][2] == false ## Creates a sequence of 20 random numbers from 1 to 10 import random var seqRand = newSeqWith(20, rand(10))
© 2006–2021 Andreas Rumpf
Licensed under the MIT License.
https://nim-lang.org/docs/sequtils.html