sequtils: findIt (#25134)

Complements `anyIt`, `find`, etc, plugging an odd gap in the `xxxIt`
family of functions
This commit is contained in:
Jacek Sieka
2025-09-09 20:05:12 +02:00
committed by GitHub
parent c8456eacd5
commit 5ba279276e
2 changed files with 52 additions and 21 deletions

View File

@@ -116,6 +116,11 @@ macro evalOnceAs(expAlias, exp: untyped,
newProc(name = genSym(nskTemplate, $expAlias), params = [getType(untyped)],
body = val, procType = nnkTemplateDef))
template unCheckedInc(x) =
{.push overflowChecks: off.}
inc(x)
{.pop.}
func 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.
@@ -139,7 +144,7 @@ func concat*[T](seqs: varargs[seq[T]]): seq[T] =
for s in items(seqs):
for itm in items(s):
result[i] = itm
inc(i)
unCheckedInc(i)
func addUnique*[T](s: var seq[T], x: sink T) =
## Adds `x` to the container `s` if it is not already present.
@@ -170,7 +175,7 @@ func count*[T](s: openArray[T], x: T): int =
result = 0
for itm in items(s):
if itm == x:
inc result
unCheckedInc result
func cycle*[T](s: openArray[T], n: Natural): seq[T] =
## Returns a new sequence with the items of the container `s` repeated
@@ -188,7 +193,7 @@ func cycle*[T](s: openArray[T], n: Natural): seq[T] =
for x in 0 ..< n:
for e in s:
result[o] = e
inc o
unCheckedInc o
proc repeat*[T](x: T, n: Natural): seq[T] =
## Returns a new sequence with the item `x` repeated `n` times.
@@ -321,6 +326,26 @@ func minmax*[T](x: openArray[T], cmp: proc(a, b: T): int): (T, T) {.effectsOf: c
elif cmp(result[1], x[i]) < 0: result[1] = x[i]
template findIt*(s, predicate: untyped): int =
## Iterates through a container and returns the index of the first item that
## fulfills the predicate, or -1
##
## Unlike the `find`, the predicate needs to be an expression using
## the `it` variable for testing, like: `findIt([3, 2, 1], it == 2)`.
var
res = -1
i = 0
# We must use items here since both `find` and `anyIt` are defined in terms
# of `items`
# (and not `pairs`)
for it {.inject.} in items(s):
if predicate:
res = i
break
unCheckedInc(i)
res
template zipImpl(s1, s2, retType: untyped): untyped =
proc zip*[S, T](s1: openArray[S], s2: openArray[T]): retType =
## Returns a new sequence with a combination of the two input containers.
@@ -417,7 +442,7 @@ func distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
if extra == 0 or spread == false:
# Use an algorithm which overcounts the stride and minimizes reading limits.
if extra > 0: inc(stride)
if extra > 0: unCheckedInc(stride)
for i in 0 ..< num:
result[i] = newSeq[T]()
for g in first ..< min(s.len, first + stride):
@@ -429,7 +454,7 @@ func distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
last = first + stride
if extra > 0:
extra -= 1
inc(last)
unCheckedInc(last)
result[i] = newSeq[T]()
for g in first ..< last:
result[i].add(s[g])
@@ -586,7 +611,7 @@ proc keepIf*[T](s: var seq[T], pred: proc(x: T): bool {.closure.})
s[pos] = move(s[i])
else:
shallowCopy(s[pos], s[i])
inc(pos)
unCheckedInc(pos)
setLen(s, pos)
func delete*[T](s: var seq[T]; slice: Slice[int]) =
@@ -617,8 +642,8 @@ func delete*[T](s: var seq[T]; slice: Slice[int]) =
s[i] = move(s[j])
else:
s[i].shallowCopy(s[j])
inc(i)
inc(j)
unCheckedInc(i)
unCheckedInc(j)
setLen(s, newLen)
when nimvm: defaultImpl()
else:
@@ -649,8 +674,8 @@ func delete*[T](s: var seq[T]; first, last: Natural) {.deprecated: "use `delete(
s[i] = move(s[j])
else:
s[i].shallowCopy(s[j])
inc(i)
inc(j)
unCheckedInc(i)
unCheckedInc(j)
setLen(s, newLen)
func insert*[T](dest: var seq[T], src: openArray[T], pos = 0) =
@@ -681,10 +706,10 @@ func insert*[T](dest: var seq[T], src: openArray[T], pos = 0) =
dec(i)
dec(j)
# Insert items from `dest` into `dest` at `pos`
inc(j)
unCheckedInc(j)
for item in src:
dest[j] = item
inc(j)
unCheckedInc(j)
template filterIt*(s, pred: untyped): untyped =
@@ -743,7 +768,7 @@ template keepItIf*(varSeq: seq, pred: untyped) =
varSeq[pos] = move(varSeq[i])
else:
shallowCopy(varSeq[pos], varSeq[i])
inc(pos)
unCheckedInc(pos)
setLen(varSeq, pos)
since (1, 1):
@@ -842,12 +867,7 @@ template anyIt*(s, pred: untyped): bool =
assert numbers.anyIt(it > 8) == true
assert numbers.anyIt(it > 9) == false
var result = false
for it {.inject.} in items(s):
if pred:
result = true
break
result
findIt(s, pred) != -1
template toSeq1(s: not iterator): untyped =
# overload for typed but not iterator
@@ -875,7 +895,7 @@ template toSeq2(iter: iterator): untyped =
var result = newSeq[typeof(iter2)](iter2.len)
for x in iter2:
result[i] = x
inc i
unCheckedInc i
result
else:
type OutType = typeof(iter2())
@@ -920,7 +940,7 @@ template toSeq*(iter: untyped): untyped =
var i = 0
for x in iter2:
result[i] = x
inc i
unCheckedInc i
result
else:
var result: seq[typeof(iter)] = @[]

View File

@@ -258,6 +258,17 @@ block: # any
doAssert any(anumbers, proc (x: int): bool = return x > 8) == true
doAssert any(anumbers, proc (x: int): bool = return x > 9) == false
block: # findIt
let
numbers = @[1, 4, 5, 8, 9, 7, 4]
anumbers = [1, 4, 5, 8, 9, 7, 4]
len0seq: seq[int] = @[]
doAssert findIt(numbers, it == 4) == 1
doAssert findIt(numbers, it > 9) == -1
doAssert findIt(len0seq, true) == -1
doAssert findIt(anumbers, it > 8) == 4
doAssert findIt(anumbers, it > 9) == -1
block: # anyIt
let
numbers = @[1, 4, 5, 8, 9, 7, 4]