From c1ca7181fdb08ee1b52e4de5f63be3e0c0d26536 Mon Sep 17 00:00:00 2001 From: Edward Muller Date: Sat, 22 Mar 2025 17:33:43 -0700 Subject: [PATCH] more kv helpers #minor --- README.md | 3 +- seq.go | 267 +++++++++++++++++++++++++++++++++++++++--- seq_test.go | 329 +++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 579 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index bea4b8f..a8c1a74 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ # Sequence (Iterator) Utilities for Golang -Initial version. More to details soon. +Golang's "missing" iterator/sequence functions. + diff --git a/seq.go b/seq.go index 4de4a1b..485ce78 100644 --- a/seq.go +++ b/seq.go @@ -3,6 +3,7 @@ package seq import ( "cmp" "iter" + "sync/atomic" ) // With returns a sequence with the provided values. The values are iterated over lazily when the returned sequence is iterated @@ -126,14 +127,14 @@ func FilterKV[K, V any](seq iter.Seq2[K, V], fn func(K, V) bool) iter.Seq2[K, V] } } -// IterIntV converts an iter.Seq[T] to an iter.Seq2[int, T]. The provided sequence is iterated over lazily when the returned -// sequence is iterated over. -func IterIntV[T any](iter iter.Seq[T]) iter.Seq2[int, T] { - i := -1 - return IterKV(iter, func(T) int { - i++ - return i - }) +// IntK returns a function that returns an increasing integer each time it is called, starting at 0. The returned function is stateful +// and is safe to call concurrently. +func IntK[V any]() func(V) int { + var i atomic.Int64 + i.Store(-1) + return func(V) int { + return int(i.Add(1)) + } } // IterKV converts an iter.Seq[V] to an iter.Seq2[K, V]. The provided sequence is iterated over lazily when the returned @@ -178,7 +179,7 @@ func IterV[K, V any](iter iter.Seq2[K, V]) iter.Seq[V] { func Max[T cmp.Ordered](seq iter.Seq[T]) (T, bool) { var mt T var value bool - for i, t := range IterIntV(seq) { + for i, t := range IterKV(seq, IntK[T]()) { switch i { case 0: mt = t @@ -194,7 +195,7 @@ func Max[T cmp.Ordered](seq iter.Seq[T]) (T, bool) { func MaxFunc[T any](seq iter.Seq[T], compare func(T, T) int) (T, bool) { var mt T var value bool - for i, t := range IterIntV(seq) { + for i, t := range IterKV(seq, IntK[T]()) { switch i { case 0: mt = t @@ -208,12 +209,33 @@ func MaxFunc[T any](seq iter.Seq[T], compare func(T, T) int) (T, bool) { return mt, value } +// MaxFuncKV is like [MaxFunc] but for key-value pairs. The provided sequence is iterated over before MaxFuncKV returns. +func MaxFuncKV[K, V any](seq iter.Seq2[K, V], compare func(KV[K, V], KV[K, V]) int) (KV[K, V], bool) { + var mt KV[K, V] + var value bool + var i int + for k, v := range seq { + switch i { + case 0: + mt = KV[K, V]{K: k, V: v} + value = true + default: + t := KV[K, V]{K: k, V: v} + if compare(t, mt) > 0 { + mt = t + } + } + i++ + } + return mt, value +} + // Min value from the sequence. Uses min built in to compare values. The second value is false if the sequence is empty. The // sequence is iterated over before Min returns. func Min[T cmp.Ordered](seq iter.Seq[T]) (T, bool) { var mt T var value bool - for i, t := range IterIntV(seq) { + for i, t := range IterKV(seq, IntK[T]()) { switch i { case 0: mt = t @@ -229,7 +251,7 @@ func Min[T cmp.Ordered](seq iter.Seq[T]) (T, bool) { func MinFunc[T any](seq iter.Seq[T], compare func(T, T) int) (T, bool) { var mt T var value bool - for i, t := range IterIntV(seq) { + for i, t := range IterKV(seq, IntK[T]()) { switch i { case 0: mt = t @@ -243,6 +265,27 @@ func MinFunc[T any](seq iter.Seq[T], compare func(T, T) int) (T, bool) { return mt, value } +// MinFuncKV is like [MinFunc] but for key-value pairs. The provided sequence is iterated over before MinFuncKV returns. +func MinFuncKV[K, V any](seq iter.Seq2[K, V], compare func(KV[K, V], KV[K, V]) int) (KV[K, V], bool) { + var mt KV[K, V] + var value bool + var i int + for k, v := range seq { + switch i { + case 0: + mt = KV[K, V]{K: k, V: v} + value = true + default: + t := KV[K, V]{K: k, V: v} + if compare(t, mt) < 0 { + mt = t + } + } + i++ + } + return mt, value +} + // Reduce the sequence to a single value by applying the function fn to each value. The provided sequence is iterated // over before Reduce returns. func Reduce[T, O any](seq iter.Seq[T], initial O, fn func(agg O, t T) O) O { @@ -268,7 +311,7 @@ func ReduceKV[K, V, O any](seq iter.Seq2[K, V], initial O, fn func(agg O, k K, v func Compact[T comparable](seq iter.Seq[T]) iter.Seq[T] { return func(yield func(T) bool) { var prev T - for i, t := range IterIntV(seq) { + for i, t := range IterKV(seq, IntK[T]()) { switch i { case 0: prev = t @@ -293,7 +336,7 @@ func Compact[T comparable](seq iter.Seq[T]) iter.Seq[T] { func CompactFunc[T any](seq iter.Seq[T], equal func(T, T) bool) iter.Seq[T] { return func(yield func(T) bool) { var prev T - for i, t := range IterIntV(seq) { + for i, t := range IterKV(seq, IntK[T]()) { switch i { case 0: prev = t @@ -312,6 +355,41 @@ func CompactFunc[T any](seq iter.Seq[T], equal func(T, T) bool) iter.Seq[T] { } } +// CompactKV returns an iterator that yields all key-value pairs that are not equal to the previous key-value pair. The provided +// sequence is iterated over lazily when the returned sequence is iterated over. +func CompactKV[K, V comparable](seq iter.Seq2[K, V]) iter.Seq2[K, V] { + return func(yield func(K, V) bool) { + var prev KV[K, V] + for k, v := range seq { + if prev.K != k || prev.V != v { + prev.K = k + prev.V = v + if !yield(k, v) { + return + } + } + } + } +} + +// CompactKVFunc is like [CompactKV] but uses the function to compare key-value pairs. For runs of key-value pairs that compare +// equal, CompactKVFunc only yields the first one. The provided sequence is iterated over lazily when the returned sequence is +// iterated over. +func CompactKVFunc[K, V any](seq iter.Seq2[K, V], equal func(KV[K, V], KV[K, V]) bool) iter.Seq2[K, V] { + return func(yield func(K, V) bool) { + var prev KV[K, V] + for k, v := range seq { + if !equal(prev, KV[K, V]{K: k, V: v}) { + prev.K = k + prev.V = v + if !yield(k, v) { + return + } + } + } + } +} + // Chunk the sequence into chunks of size. The provided sequence is iterated over lazily when the returned sequence is iterated // over. The last chunk may have fewer than size elements. func Chunk[T any](seq iter.Seq[T], size int) iter.Seq[iter.Seq[T]] { @@ -332,6 +410,26 @@ func Chunk[T any](seq iter.Seq[T], size int) iter.Seq[iter.Seq[T]] { } } +// ChunkKV is like [Chunk] but for key-value pairs. The provided sequence is iterated over lazily when the returned sequence is +// iterated over. The last chunk may have fewer than size elements. +func ChunkKV[K, V any](seq iter.Seq2[K, V], size int) iter.Seq[iter.Seq2[K, V]] { + return func(yield func(iter.Seq2[K, V]) bool) { + var chunk []KV[K, V] + for k, v := range seq { + chunk = append(chunk, KV[K, V]{K: k, V: v}) + if len(chunk) == size { + if !yield(WithKV(chunk...)) { + return + } + chunk = nil + } + } + if len(chunk) > 0 { + yield(WithKV(chunk...)) + } + } +} + // Compare is like [CompareFunc] but uses the cmp.Compare function to compare elements. func Compare[T cmp.Ordered](a, b iter.Seq[T]) int { return CompareFunc(a, b, cmp.Compare) @@ -377,6 +475,56 @@ func CompareFunc[T any](a, b iter.Seq[T], compare func(T, T) int) int { return 0 } +// CompareKV is like [CompareKVFunc] but uses the cmp.Compare function to compare keys and values. +func CompareKV[K, V cmp.Ordered](a, b iter.Seq2[K, V]) int { + return CompareKVFunc(a, b, func(a, b KV[K, V]) int { + if cmp.Compare(a.K, b.K) == 0 { + return cmp.Compare(a.V, b.V) + } + return cmp.Compare(a.K, b.K) + }) +} + +// CompareKVFunc compares the key-value pairs of a and b, using the compare func on each pair of key-value pairs. The key-value +// pairs are compared sequentially, until one key-value pair is not equal to the other. The result of comparing the first +// non-matching key-value pairs is returned. If both sequences are equal until one of them ends, the shorter sequence is +// considered less than the longer one. The result is 0 if a == b, -1 if a < b, and +1 if a > b. +func CompareKVFunc[AK, AV, BK, BV any](a iter.Seq2[AK, AV], b iter.Seq2[BK, BV], compare func(a KV[AK, AV], b KV[BK, BV]) int) int { + bvals := make(chan KV[BK, BV]) + exit := make(chan struct{}) + defer close(exit) + + go func() { + defer close(bvals) + for k, v := range b { + select { + case bvals <- KV[BK, BV]{k, v}: + case <-exit: + return + } + } + }() + + for ak, av := range a { + bv, ok := <-bvals + if !ok { // b is shorter than a + return 1 + } + if c := compare(KV[AK, AV]{ak, av}, bv); c != 0 { + return c + } + } + + // done with a, check if b is longer + // if bvals isn't closed b is longer than a + if _, ok := <-bvals; ok { + return -1 + } + + // a and b are equal + return 0 +} + // Contains returns true if the value is in the sequence. The sequence is iterated over when Contains is called. func Contains[T comparable](seq iter.Seq[T], value T) bool { for t := range seq { @@ -387,6 +535,16 @@ func Contains[T comparable](seq iter.Seq[T], value T) bool { return false } +// ContainsKV returns true if the key-value pair is in the sequence. The sequence is iterated over when ContainsKV is called. +func ContainsKV[K, V comparable](seq iter.Seq2[K, V], key K, value V) bool { + for k, v := range seq { + if k == key && v == value { + return true + } + } + return false +} + // ContainsFunc returns true if the function returns true for any value in the sequence. The sequence is iterated over when // ContainsFunc is called. func ContainsFunc[T any](seq iter.Seq[T], equal func(T) bool) bool { @@ -398,6 +556,17 @@ func ContainsFunc[T any](seq iter.Seq[T], equal func(T) bool) bool { return false } +// ContainsKVFunc returns true if the function returns true for any key-value pair in the sequence. The sequence is iterated over +// when ContainsKVFunc is called. +func ContainsKVFunc[K, V any](seq iter.Seq2[K, V], equal func(K, V) bool) bool { + for k, v := range seq { + if equal(k, v) { + return true + } + } + return false +} + // Equal returns true if the sequences are equal. The sequences are compared sequentially, until one element is not equal to // the other. func Equal[T comparable](a, b iter.Seq[T]) bool { @@ -409,6 +578,20 @@ func Equal[T comparable](a, b iter.Seq[T]) bool { }) == 0 } +// EqualKV returns true if the key-value pairs in the sequences are equal. The key-value pairs are compared sequentially, until +// one key-value pair is not equal to the other. +func EqualKV[K, V comparable](a, b iter.Seq2[K, V]) bool { + return CompareKVFunc(a, b, func(a, b KV[K, V]) int { + if a.K == b.K && a.V == b.V { + return 0 + } + if a.K != b.K { + return -1 + } + return 1 + }) == 0 +} + // EqualFunc is like [Equal] but uses the function to compare elements. func EqualFunc[T any](a, b iter.Seq[T], equal func(T, T) bool) bool { return CompareFunc(a, b, func(a, b T) int { @@ -419,6 +602,16 @@ func EqualFunc[T any](a, b iter.Seq[T], equal func(T, T) bool) bool { }) == 0 } +// EqualKVFunc is like [EqualKV] but uses the function to compare key-value pairs. +func EqualKVFunc[AK, AV, BK, BV any](a iter.Seq2[AK, AV], b iter.Seq2[BK, BV], equal func(a KV[AK, AV], b KV[BK, BV]) bool) bool { + return CompareKVFunc(a, b, func(a KV[AK, AV], b KV[BK, BV]) int { + if equal(a, b) { + return 0 + } + return 1 + }) == 0 +} + // Repeat returns a sequence which repeats the value n times. func Repeat[T any](n int, t T) iter.Seq[T] { return func(yield func(T) bool) { @@ -430,6 +623,17 @@ func Repeat[T any](n int, t T) iter.Seq[T] { } } +// RepeatKV returns a sequence which repeats the key-value pair n times. +func RepeatKV[K, V any](n int, k K, v V) iter.Seq2[K, V] { + return func(yield func(K, V) bool) { + for i := 0; i < n; i++ { + if !yield(k, v) { + return + } + } + } +} + // Replace the old value with the new value in the sequence. The provided sequence is iterated over lazily when the // returned sequence is iterated over. func Replace[T comparable](seq iter.Seq[T], old, new T) iter.Seq[T] { @@ -445,10 +649,27 @@ func Replace[T comparable](seq iter.Seq[T], old, new T) iter.Seq[T] { } } -// IsSorted returns true if the sequence is sorted. The provided sequence is iterated over before IsSorted returns. +// ReplaceKV replaces the old key-value pair with the new key-value pair in the sequence. The provided sequence is iterated +// over lazily when the returned sequence is iterated over. +func ReplaceKV[K, V comparable](seq iter.Seq2[K, V], old KV[K, V], new KV[K, V]) iter.Seq2[K, V] { + return func(yield func(K, V) bool) { + for k, v := range seq { + if k == old.K && v == old.V { + k = new.K + v = new.V + } + if !yield(k, v) { + return + } + } + } +} + +// IsSorted returns true if the sequence is sorted. The provided sequence is iterated over before IsSorted returns. [cmp.Compare] +// // is used to compare elements. func IsSorted[T cmp.Ordered](seq iter.Seq[T]) bool { var prev T - for i, t := range IterIntV(seq) { + for i, t := range IterKV(seq, IntK[T]()) { switch i { case 0: prev = t @@ -461,3 +682,17 @@ func IsSorted[T cmp.Ordered](seq iter.Seq[T]) bool { } return true } + +// IsSortedKV returns true if the sequence is sorted. The provided sequence is iterated over before IsSortedKV returns. +// [cmp.Compare] is used to compare keys and values +func IsSortedKV[K, V cmp.Ordered](seq iter.Seq2[K, V]) bool { + var prev KV[K, V] + for k, v := range seq { + if (cmp.Compare(k, prev.K) < 0) || (cmp.Compare(v, prev.V) < 0) { + return false + } + prev.K = k + prev.V = v + } + return true +} diff --git a/seq_test.go b/seq_test.go index b3e3e45..29d4f8a 100644 --- a/seq_test.go +++ b/seq_test.go @@ -151,6 +151,31 @@ func ExampleMinFunc() { // false } +func ExampleMinFuncKV() { + type tKV = KV[string, int] + i := WithKV(tKV{K: "a", V: 1}, tKV{K: "b", V: 2}, tKV{K: "c", V: 3}) + + fmt.Println(MinFuncKV(i, func(a tKV, b tKV) int { + return a.V - b.V + })) + + fmt.Println(MinFuncKV(i, func(a tKV, b tKV) int { + return strings.Compare(a.K, b.K) + })) + + fmt.Println(MinFuncKV(i, func(a tKV, b tKV) int { + if a.V == 3 { // pretend any value of 3 is the min + return -1 + } + return 1 + })) + + // Output: + // {a 1} true + // {a 1} true + // {c 3} true +} + func ExampleMax() { i := With(9, 8, 7, 6, 5, 4, 3, 2, 1) @@ -177,6 +202,31 @@ func ExampleMaxFunc() { // false } +func ExampleMaxFuncKV() { + type tKV = KV[string, int] + i := WithKV(tKV{K: "a", V: 1}, tKV{K: "b", V: 2}, tKV{K: "c", V: 3}) + + fmt.Println(MaxFuncKV(i, func(a tKV, b tKV) int { + return a.V - b.V + })) + + fmt.Println(MaxFuncKV(i, func(a tKV, b tKV) int { + return strings.Compare(a.K, b.K) + })) + + fmt.Println(MaxFuncKV(i, func(a tKV, b tKV) int { + if a.V == 1 { // pretend any value of 1 is the max + return 1 + } + return -1 + })) + + // Output: + // {c 3} true + // {c 3} true + // {a 1} true +} + func ExampleReduce() { i := With(1, 2, 3, 4, 5) @@ -210,19 +260,26 @@ func ExampleReduceKV() { // hello: a1b2c3 } -func ExampleIterIntV() { +func ExampleIterKV() { i := With(1, 2, 3, 4) - s := IterIntV(i) - for i, v := range s { + for i, v := range IterKV(i, IntK[int]()) { fmt.Printf("%d: %d\n", i, v) } + for i, v := range IterKV(i, strconv.Itoa) { + fmt.Printf("%s: %d\n", i, v) + } + // Output: // 0: 1 // 1: 2 // 2: 3 // 3: 4 + // 1: 1 + // 2: 2 + // 3: 3 + // 4: 4 } func ExampleIterK() { @@ -283,6 +340,51 @@ func ExampleCompactFunc() { // 5 } +func ExampleCompactKV() { + type tKV = KV[string, int] + i := WithKV( + tKV{K: "a", V: 1}, + tKV{K: "a", V: 2}, + tKV{K: "a", V: 2}, + tKV{K: "b", V: 3}, + tKV{K: "b", V: 3}, + tKV{K: "c", V: 4}, + ) + + for k, v := range CompactKV(i) { + fmt.Println(k, v) + } + + // Output: + // a 1 + // a 2 + // b 3 + // c 4 +} + +func ExampleCompactKVFunc() { + type tKV = KV[string, int] + i := WithKV( + tKV{K: "a", V: 1}, + tKV{K: "a", V: 2}, + tKV{K: "a", V: 2}, + tKV{K: "b", V: 3}, + tKV{K: "b", V: 3}, + tKV{K: "c", V: 4}, + ) + + for k, v := range CompactKVFunc(i, func(a, b tKV) bool { + return a.K == b.K + }) { + fmt.Println(k, v) + } + + // Output: + // a 1 + // b 3 + // c 4 +} + func ExampleChunk() { i := With(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) @@ -297,6 +399,34 @@ func ExampleChunk() { // [10 11] } +func ExampleChunkKV() { + type tKV = KV[string, int] + itr := WithKV( + tKV{K: "a", V: 1}, + tKV{K: "a", V: 2}, + tKV{K: "a", V: 2}, + tKV{K: "b", V: 3}, + tKV{K: "b", V: 3}, + tKV{K: "c", V: 4}, + tKV{K: "c", V: 5}, + ) + + var i int + for chunk := range ChunkKV(itr, 3) { + fmt.Printf("Chunk %d: ", i) + for k, v := range chunk { + fmt.Printf("(%s %d)", k, v) + } + fmt.Println() + i++ + } + + // Output: + // Chunk 0: (a 1)(a 2)(a 2) + // Chunk 1: (b 3)(b 3)(c 4) + // Chunk 2: (c 5) +} + func ExampleCompare() { a := With(1, 2, 3) b := With(1, 2, 3) @@ -347,6 +477,78 @@ func ExampleCompareFunc() { // -1 } +func ExampleCompareKV() { + type tKV = KV[string, int] + a := WithKV(tKV{K: "a", V: 1}, tKV{K: "b", V: 2}, tKV{K: "c", V: 3}) + b := WithKV(tKV{K: "a", V: 1}, tKV{K: "b", V: 2}, tKV{K: "c", V: 3}) + fmt.Println(CompareKV(a, b)) + + c := WithKV(tKV{K: "a", V: 1}, tKV{K: "b", V: 2}) + fmt.Println(CompareKV(a, c)) + + d := WithKV(tKV{K: "a", V: 1}, tKV{K: "b", V: 2}, tKV{K: "c", V: 4}) + fmt.Println(CompareKV(a, d)) + + e := WithKV(tKV{K: "a", V: 1}, tKV{K: "b", V: 2}, tKV{K: "e", V: 3}) + fmt.Println(CompareKV(a, e)) + + f := WithKV(tKV{K: "a", V: 1}, tKV{K: "b", V: 2}, tKV{K: "c", V: 3}, tKV{K: "d", V: 4}) + fmt.Println(CompareKV(a, f)) + + // Output: + // 0 + // 1 + // -1 + // -1 + // -1 +} + +func ExampleCompareKVFunc() { + type aKV = KV[string, int] + a := WithKV(aKV{K: "a", V: 1}, aKV{K: "b", V: 2}, aKV{K: "c", V: 3}) + b := WithKV(aKV{K: "a", V: 1}, aKV{K: "b", V: 2}, aKV{K: "c", V: 3}) + fmt.Println(CompareKVFunc(a, b, func(a aKV, b aKV) int { + return a.V - b.V + })) + + c := WithKV(aKV{K: "a", V: 1}, aKV{K: "b", V: 2}) + fmt.Println(CompareKVFunc(a, c, func(a aKV, b aKV) int { + return strings.Compare(a.K, b.K) + })) + + d := WithKV(aKV{K: "a", V: 1}, aKV{K: "b", V: 2}, aKV{K: "c", V: 4}) + fmt.Println(CompareKVFunc(a, d, func(a aKV, b aKV) int { + return a.V - b.V + })) + + e := WithKV(aKV{K: "a", V: 1}, aKV{K: "b", V: 2}, aKV{K: "e", V: 3}) + fmt.Println(CompareKVFunc(a, e, func(a aKV, b aKV) int { + return strings.Compare(a.K, b.K) + })) + + f := WithKV(aKV{K: "a", V: 1}, aKV{K: "b", V: 2}, aKV{K: "c", V: 3}, aKV{K: "d", V: 4}) + fmt.Println(CompareKVFunc(a, f, func(a aKV, b aKV) int { + return a.V - b.V + })) + + type bKV = KV[string, string] + g := WithKV(bKV{K: "a", V: "1"}, bKV{K: "b", V: "2"}, bKV{K: "c", V: "3"}) + fmt.Println(CompareKVFunc(a, g, func(a aKV, b bKV) int { + if c := strings.Compare(a.K, b.K); c != 0 { + return c + } + return strings.Compare(strconv.Itoa(a.V), b.V) + })) + + // Output: + // 0 + // 1 + // -1 + // -1 + // -1 + // 0 +} + func ExampleContains() { i := With(1, 2, 3, 4, 5) @@ -358,6 +560,18 @@ func ExampleContains() { // false } +func ExampleContainsKV() { + type tKV = KV[string, int] + i := WithKV(tKV{K: "a", V: 1}, tKV{K: "b", V: 2}, tKV{K: "c", V: 3}) + + fmt.Println(ContainsKV(i, "b", 2)) + fmt.Println(ContainsKV(i, "d", 1)) + + // Output: + // true + // false +} + func ExampleContainsFunc() { i := With("hi", "hello", "world") @@ -369,6 +583,18 @@ func ExampleContainsFunc() { // false } +func ExampleContainsKVFunc() { + type tKV = KV[string, int] + i := WithKV(tKV{K: "a", V: 1}, tKV{K: "b", V: 2}, tKV{K: "c", V: 3}) + + fmt.Println(ContainsKVFunc(i, func(k string, v int) bool { return k == "b" && v == 2 })) + fmt.Println(ContainsKVFunc(i, func(k string, v int) bool { return k == "d" && v == 1 })) + + // Output: + // true + // false +} + func ExampleEqual() { a := With(1, 2, 3) b := With(1, 2, 3) @@ -386,6 +612,28 @@ func ExampleEqual() { // false } +func ExampleEqualKV() { + type tKV = KV[string, int] + a := WithKV(tKV{K: "a", V: 1}, tKV{K: "b", V: 2}, tKV{K: "c", V: 3}) + b := WithKV(tKV{K: "a", V: 1}, tKV{K: "b", V: 2}, tKV{K: "c", V: 3}) + fmt.Println(EqualKV(a, b)) + + c := WithKV(tKV{K: "a", V: 1}, tKV{K: "b", V: 2}) + fmt.Println(EqualKV(a, c)) + + d := WithKV(tKV{K: "c", V: 3}, tKV{K: "b", V: 2}, tKV{K: "a", V: 1}) + fmt.Println(EqualKV(a, d)) + + e := WithKV(tKV{K: "a", V: 1}, tKV{K: "b", V: 2}, tKV{K: "c", V: 3}, tKV{K: "d", V: 4}) + fmt.Println(EqualKV(a, e)) + + // Output: + // true + // false + // false + // false +} + func ExampleEqualFunc() { a := With("hi", "hello", "world") b := With("hi", "hello", "world") @@ -407,6 +655,42 @@ func ExampleEqualFunc() { // true } +func ExampleEqualKVFunc() { + type tKV = KV[string, int] + a := WithKV(tKV{K: "a", V: 1}, tKV{K: "b", V: 2}, tKV{K: "c", V: 3}) + b := WithKV(tKV{K: "a", V: 1}, tKV{K: "b", V: 2}, tKV{K: "c", V: 3}) + fmt.Println(EqualKVFunc(a, b, func(a tKV, b tKV) bool { + return a.V == b.V && a.K == b.K + })) + + c := WithKV(tKV{K: "a", V: 1}, tKV{K: "b", V: 2}) + fmt.Println(EqualKVFunc(a, c, func(a tKV, b tKV) bool { + return a.V == b.V && a.K == b.K + })) + + d := WithKV(tKV{K: "c", V: 3}, tKV{K: "b", V: 2}, tKV{K: "a", V: 1}) + fmt.Println(EqualKVFunc(a, d, func(a tKV, b tKV) bool { + return a.V == b.V && a.K == b.K + })) + + e := WithKV(tKV{K: "a", V: 1}, tKV{K: "b", V: 2}, tKV{K: "c", V: 3}, tKV{K: "d", V: 4}) + fmt.Println(EqualKVFunc(a, e, func(a tKV, b tKV) bool { + return a.V == b.V && a.K == b.K + })) + + f := WithKV(tKV{K: "A", V: 1}, tKV{K: "B", V: 2}, tKV{K: "C", V: 3}) + fmt.Println(EqualKVFunc(a, f, func(a tKV, b tKV) bool { + return a.V == b.V && strings.EqualFold(a.K, b.K) + })) + + // Output: + // true + // false + // false + // false + // true +} + func ExampleRepeat() { i := Repeat(3, "hi") for v := range i { @@ -431,6 +715,24 @@ func ExampleReplace() { // [1 6 3 7 5] } +func ExampleReplaceKV() { + type tKV = KV[string, int] + i := WithKV(tKV{K: "a", V: 1}, tKV{K: "b", V: 2}, tKV{K: "c", V: 3}) + + i = ReplaceKV(i, tKV{"a", 1}, tKV{"a", 6}) + i = ReplaceKV(i, tKV{"c", 7}, tKV{"c", 8}) // no effect + + for k, v := range i { + fmt.Println(k, v) + } + fmt.Println() + + // Output: + // a 6 + // b 2 + // c 3 +} + func ExampleIsSorted() { i := With(1, 2, 3, 4, 5) fmt.Println(IsSorted(i)) @@ -444,3 +746,24 @@ func ExampleIsSorted() { // true // false } + +func ExampleIsSortedKV() { + type kv = KV[string, int] + i := WithKV(kv{K: "a", V: 1}, kv{K: "b", V: 2}, kv{K: "c", V: 3}) + fmt.Println(IsSortedKV(i)) + + i = WithKV(kv{K: "a", V: 1}, kv{K: "b", V: 2}, kv{K: "b", V: 2}, kv{K: "c", V: 3}) + fmt.Println(IsSortedKV(i)) + + i = WithKV(kv{K: "a", V: 1}, kv{K: "b", V: 2}, kv{K: "c", V: 3}, kv{K: "d", V: 2}) + fmt.Println(IsSortedKV(i)) + + i = WithKV(kv{"b", 1}, kv{"a", 2}, kv{"c", 3}) + fmt.Println(IsSortedKV(i)) + + // Output: + // true + // true + // false + // false +}