Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add algorithm.merge #16182

Merged
merged 18 commits into from
Mar 23, 2021
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@

- `writeStackTrace` is available in JS backend now.

- Added `algorithm.merge`.

## Language changes

- `nimscript` now handles `except Exception as e`.
Expand Down
62 changes: 62 additions & 0 deletions lib/pure/algorithm.nim
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
## * `sequtils module<sequtils.html>`_ for working with the built-in seq type
## * `tables module<tables.html>`_ for sorting tables

import std/private/since
ringabout marked this conversation as resolved.
Show resolved Hide resolved

type
SortOrder* = enum
Descending, Ascending
Expand Down Expand Up @@ -556,6 +558,66 @@ proc isSorted*[T](a: openArray[T], order = SortOrder.Ascending): bool =
assert isSorted(e) == false
isSorted(a, system.cmp[T], order)

proc merge*[T](
timotheecour marked this conversation as resolved.
Show resolved Hide resolved
x, y: openArray[T], cmp: proc(x, y: T
): int {.closure.}, order = SortOrder.Ascending): seq[T] {.since: (1, 5, 1).} =
## Merges two sorted `openArray`. All of inputs are assumed to be sorted.
ringabout marked this conversation as resolved.
Show resolved Hide resolved
ringabout marked this conversation as resolved.
Show resolved Hide resolved
## If you do not wish to provide your own ``cmp``,
ringabout marked this conversation as resolved.
Show resolved Hide resolved
ringabout marked this conversation as resolved.
Show resolved Hide resolved
## you may use `system.cmp` or instead call the overloaded
## version of `merge`, which uses `system.cmp`.
##
## **See also:**
## * `merge proc<#merge,openArray[T],openArray[T]>`_
runnableExamples:
let x = @[1, 3, 6]
let y = @[2, 3, 4]

let res = x.merge(y, system.cmp[int])
assert res.isSorted
assert res == @[1, 2, 3, 3, 4, 6]
let
size_x = x.len
size_y = y.len

result = newSeqOfCap[T](size_x + size_y)
ringabout marked this conversation as resolved.
Show resolved Hide resolved

var
index_x = 0
index_y = 0

ringabout marked this conversation as resolved.
Show resolved Hide resolved
while true:
if index_x == size_x:
result.add y[index_y .. ^1]
ringabout marked this conversation as resolved.
Show resolved Hide resolved
return

if index_y == size_y:
result.add x[index_x .. ^1]
ringabout marked this conversation as resolved.
Show resolved Hide resolved
return

let item_x = x[index_x]
let item_y = y[index_y]

if cmp(item_x, item_y) * order > 0:
result.add item_y
ringabout marked this conversation as resolved.
Show resolved Hide resolved
inc index_y
else:
result.add item_x
ringabout marked this conversation as resolved.
Show resolved Hide resolved
inc index_x

proc merge*[T](x, y: openArray[T], order = SortOrder.Ascending): seq[T] {.since: (1, 5, 1).} =
ringabout marked this conversation as resolved.
Show resolved Hide resolved
## Shortcut version of ``merge`` that uses ``system.cmp[T]`` as the comparison function.
ringabout marked this conversation as resolved.
Show resolved Hide resolved
##
## **See also:**
## * `merge proc<#merge,openArray[T],openArray[T],proc(T,T)>`_
runnableExamples:
let x = [5,10,15,20,25]
let y = [50,40,30,20,10].sorted
ringabout marked this conversation as resolved.
Show resolved Hide resolved

let res = x.merge(y)
assert res.isSorted
assert res == @[5, 10, 10, 15, 20, 20, 25, 30, 40, 50]
merge(x, y, system.cmp, order)

proc product*[T](x: openArray[seq[T]]): seq[seq[T]] =
## Produces the Cartesian product of the array. Warning: complexity
## may explode.
Expand Down
79 changes: 79 additions & 0 deletions tests/stdlib/talgorithm.nim
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,82 @@ block:
doAssert binarySearch(moreData, 6) == -1
doAssert binarySearch(moreData, 4711) == 4
doAssert binarySearch(moreData, 4712) == -1

# merge
ringabout marked this conversation as resolved.
Show resolved Hide resolved
block:
ringabout marked this conversation as resolved.
Show resolved Hide resolved
var x = @[1, 7, 8, 11, 21, 33, 45, 99]
var y = @[6, 7, 9, 12, 57, 66]

let merged = merge(x, y)
doAssert merged.issorted
ringabout marked this conversation as resolved.
Show resolved Hide resolved
doAssert merged == @[1, 6, 7, 7, 8, 9, 11, 12, 21, 33, 45, 57, 66, 99]
ringabout marked this conversation as resolved.
Show resolved Hide resolved

block:
var x = @[111, 88, 76, 56, 45, 31, 22, 19, 11, 3]
var y = @[99, 85, 83, 82, 69, 64, 48, 42, 33, 31, 26, 13]

let merged = merge(x, y, SortOrder.Descending)
doAssert merged.issorted(SortOrder.Descending)
ringabout marked this conversation as resolved.
Show resolved Hide resolved
doAssert merged == @[111, 99, 88, 85, 83, 82, 76, 69, 64, 56, 48, 45, 42, 33, 31, 31, 26, 22, 19, 13, 11, 3]
# doAssert merged == @[99, 85, 83, 82, 69, 64, 48, 42, 33, 31, 26, 13, 111, 88, 76, 56, 45, 31, 22, 19, 11, 3]

block:
var x: seq[int] = @[]
var y = @[1]

let merged = merge(x, y)
doAssert merged.issorted
doAssert merged.issorted(SortOrder.Descending)
ringabout marked this conversation as resolved.
Show resolved Hide resolved
doAssert merged == @[1]

block:
var x = [1, 3, 5, 5, 7]
var y: seq[int] = @[]

let merged = merge(x, y)
doAssert merged.issorted
ringabout marked this conversation as resolved.
Show resolved Hide resolved
doAssert merged == @x

block:
var x: array[0, int]
var y = [1, 4, 6, 7, 9]

let merged = merge(x, y)
doAssert merged.issorted
ringabout marked this conversation as resolved.
Show resolved Hide resolved
doAssert merged == @y

block:
var x: array[0, int]
var y: array[0, int]

let merged = merge(x, y)
doAssert merged.issorted
ringabout marked this conversation as resolved.
Show resolved Hide resolved
doAssert merged.len == 0

block:
var x: seq[int]
var y: seq[int]

let merged = merge(x, y)
doAssert merged.issorted
ringabout marked this conversation as resolved.
Show resolved Hide resolved
doAssert merged.len == 0

block:
type
Record = object
id: int

proc r(id: int): Record =
Record(id: id)

proc cmp(x, y: Record): int =
ringabout marked this conversation as resolved.
Show resolved Hide resolved
if x.id == y.id: return 0
if x.id < y.id: return -1
result = 1

var x = @[r(-12), r(1), r(3), r(8), r(13), r(88)]
var y = @[r(4), r(7), r(12), r(13), r(77), r(99)]

let merged = merge(x, y, cmp)
doAssert merged.issorted(cmp)
ringabout marked this conversation as resolved.
Show resolved Hide resolved
doAssert merged.len == 12