Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,7 @@ test:
bench:
$(GOCMD) test -run=NONE -bench=. -benchmem ./...

.PHONY: linters-install lint test bench
lint:
golangci-lint run

.PHONY: lint test bench
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# pkg

![Project status](https://img.shields.io/badge/version-5.10.0-green.svg)
![Project status](https://img.shields.io/badge/version-5.11.0-green.svg)
[![Build Status](https://travis-ci.org/go-playground/pkg.svg?branch=master)](https://travis-ci.org/go-playground/pkg)
[![Coverage Status](https://coveralls.io/repos/github/go-playground/pkg/badge.svg?branch=master)](https://coveralls.io/github/go-playground/pkg?branch=master)
[![GoDoc](https://godoc.org/github.com/go-playground/pkg?status.svg)](https://pkg.go.dev/mod/github.com/go-playground/pkg/v5)
Expand Down
23 changes: 23 additions & 0 deletions map/map.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//go:build go1.18
// +build go1.18

package mapext

// Retain retains only the elements specified by the function and removes others.
func Retain[K comparable, V any](m map[K]V, fn func(key K, value V) bool) {
for k, v := range m {
if fn(k, v) {
continue
}
delete(m, k)
}
}

// Map allows mapping of a map[K]V -> U.
func Map[K comparable, V any, U any](m map[K]V, init U, fn func(accum U, key K, value V) U) U {
accum := init
for k, v := range m {
accum = fn(accum, k, v)
}
return accum
}
49 changes: 49 additions & 0 deletions map/map_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//go:build go1.18
// +build go1.18

package mapext

import (
. "github.com/go-playground/assert/v2"
"sort"
"testing"
)

func TestRetain(t *testing.T) {
m := map[string]int{
"0": 0,
"1": 1,
"2": 2,
"3": 3,
}
Retain(m, func(key string, value int) bool {
return value < 1 || value > 2
})
Equal(t, len(m), 2)
Equal(t, m["0"], 0)
Equal(t, m["3"], 3)
}

func TestMap(t *testing.T) {
// Test Map to slice
m := map[string]int{
"0": 0,
"1": 1,
}
slice := Map(m, make([]int, 0, len(m)), func(accum []int, key string, value int) []int {
return append(accum, value)
})
sort.SliceStable(slice, func(i, j int) bool {
return i < j
})
Equal(t, len(slice), 2)

// Test Map to Map of different type
inverted := Map(m, make(map[int]string, len(m)), func(accum map[int]string, key string, value int) map[int]string {
accum[value] = key
return accum
})
Equal(t, len(inverted), 2)
Equal(t, inverted[0], "0")
Equal(t, inverted[1], "1")
}
82 changes: 82 additions & 0 deletions slice/slice.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//go:build go1.18
// +build go1.18

package sliceext

import (
optionext "github.com/go-playground/pkg/v5/values/option"
"sort"
)

// Retain retains only the elements specified by the function.
//
// This shuffles and returns the retained values of the slice.
func Retain[T any](slice []T, fn func(v T) bool) []T {
var j int
for _, v := range slice {
if fn(v) {
slice[j] = v
j++
}
}
return slice[:j]
}

// Filter filters out the elements specified by the function.
//
// This shuffles and returns the retained values of the slice.
func Filter[T any](slice []T, fn func(v T) bool) []T {
var j int
for _, v := range slice {
if fn(v) {
continue
}
slice[j] = v
j++
}
return slice[:j]
}

// Map maps a slice of []T -> []U using the map function.
func Map[T, U any](slice []T, init U, fn func(accum U, v T) U) U {
if len(slice) == 0 {
return init
}
accum := init
for _, v := range slice {
accum = fn(accum, v)
}
return accum
}

// Sort sorts the sliceWrapper x given the provided less function.
//
// The sort is not guaranteed to be stable: equal elements
// may be reversed from their original order.
//
// For a stable sort, use SortStable.
func Sort[T any](slice []T, less func(i T, j T) bool) {
sort.Slice(slice, func(j, k int) bool {
return less(slice[j], slice[k])
})
}

// SortStable sorts the sliceWrapper x using the provided less
// function, keeping equal elements in their original order.
func SortStable[T any](slice []T, less func(i T, j T) bool) {
sort.SliceStable(slice, func(j, k int) bool {
return less(slice[j], slice[k])
})
}

// Reduce reduces the elements to a single one, by repeatedly applying a reducing function.
func Reduce[T any](slice []T, fn func(accum T, current T) T) optionext.Option[T] {
if len(slice) == 0 {
return optionext.None[T]()
}
accum := slice[0]
for _, v := range slice {
accum = fn(accum, v)
}
return optionext.Some(accum)
}
81 changes: 81 additions & 0 deletions slice/slice_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//go:build go1.18
// +build go1.18

package sliceext

import (
. "github.com/go-playground/assert/v2"
optionext "github.com/go-playground/pkg/v5/values/option"
"strconv"
"testing"
)

func TestFilter(t *testing.T) {
s := Filter([]int{0, 1, 2, 3}, func(v int) bool {
return v > 0 && v < 3
})
Equal(t, len(s), 2)
Equal(t, s[0], 0)
Equal(t, s[1], 3)

}

func TestRetain(t *testing.T) {
s := Retain([]int{0, 1, 2, 3}, func(v int) bool {
return v > 0 && v < 3
})
Equal(t, len(s), 2)
Equal(t, s[0], 1)
Equal(t, s[1], 2)
}

func TestMap(t *testing.T) {
s := Map[int, []string]([]int{0, 1, 2, 3}, make([]string, 0, 4), func(accum []string, v int) []string {
return append(accum, strconv.Itoa(v))
})
Equal(t, len(s), 4)
Equal(t, s[0], "0")
Equal(t, s[1], "1")
Equal(t, s[2], "2")
Equal(t, s[3], "3")

// Test Map empty slice
s2 := Map[int, []string](nil, nil, func(accum []string, v int) []string {
return append(accum, strconv.Itoa(v))
})
Equal(t, len(s2), 0)
}

func TestSort(t *testing.T) {
s := []int{0, 1, 2}
Sort(s, func(i int, j int) bool {
return i > j
})
Equal(t, s[0], 2)
Equal(t, s[1], 1)
Equal(t, s[2], 0)
}

func TestSortStable(t *testing.T) {
s := []int{0, 1, 1, 2}
SortStable(s, func(i int, j int) bool {
return i > j
})
Equal(t, s[0], 2)
Equal(t, s[1], 1)
Equal(t, s[2], 1)
Equal(t, s[3], 0)
}

func TestReduce(t *testing.T) {
result := Reduce([]int{0, 1, 2}, func(accum int, current int) int {
return accum + current
})
Equal(t, result, optionext.Some(3))

// Test Reduce empty slice
result = Reduce([]int{}, func(accum int, current int) int {
return accum + current
})
Equal(t, result, optionext.None[int]())
}
1 change: 1 addition & 0 deletions sync/mutex.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//go:build go1.18
// +build go1.18

package syncext

Expand Down
1 change: 1 addition & 0 deletions sync/mutex_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//go:build go1.18
// +build go1.18

package syncext

Expand Down