pax_global_header00006660000000000000000000000064143524070420014513gustar00rootroot0000000000000052 comment=4107456c7aeb9c56439474a6917ccea36365b869 go-funk-0.9.3/000077500000000000000000000000001435240704200130725ustar00rootroot00000000000000go-funk-0.9.3/.github/000077500000000000000000000000001435240704200144325ustar00rootroot00000000000000go-funk-0.9.3/.github/workflows/000077500000000000000000000000001435240704200164675ustar00rootroot00000000000000go-funk-0.9.3/.github/workflows/go.yml000066400000000000000000000005571435240704200176260ustar00rootroot00000000000000name: Go on: push: branches: [master] pull_request: branches: [master] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Go uses: actions/setup-go@v2 with: go-version: 1.19 - name: Build run: go build -v ./... - name: Test run: make test go-funk-0.9.3/.github/workflows/golangci-lint.yml000066400000000000000000000005141435240704200217410ustar00rootroot00000000000000name: golangci-lint on: push: tags: - v* branches: - master - main pull_request: jobs: golangci: name: lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: golangci-lint uses: golangci/golangci-lint-action@v2 with: version: v1.50.1 go-funk-0.9.3/.gitignore000066400000000000000000000004301435240704200150570ustar00rootroot00000000000000# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof #GoLand .ideago-funk-0.9.3/.travis.yml000066400000000000000000000002151435240704200152010ustar00rootroot00000000000000language: go before_install: - go get golang.org/x/tools/cmd/cover - go get github.com/stretchr/testify go: - "1.16" script: make test go-funk-0.9.3/CHANGELOG.md000066400000000000000000000015671435240704200147140ustar00rootroot00000000000000go-funk changelog ================= 0.1 (2017-01-18) ---------------- Changes can be seen [here](https://github.com/thoas/go-funk/compare/73b8ae1f6443c9d4acbdc612bbb2ca804bb39b1d...master) * Better test suite * Better documentation * Add typesafe implementations: * ``Contains`` * ``Sum`` * ``Reverse`` * ``IndexOf`` * ``Uniq`` * ``Shuffle`` * Add benchmarks * ``Contains`` * ``Uniq`` * ``Sum`` * Fix ``redirectValue`` when using a circular reference * Add ``Sum`` generic implementation which computes the sum of values in an array * Add ``Tail`` generic implementation to retrieve all but the first element of array * Add ``Initial`` generic implementation to retrieve all but the last element of array * Add ``Last`` generic implementation to retrieve the last element of an array * Add ``Head`` generic implementation to retrieve the first element of an array go-funk-0.9.3/LICENSE000066400000000000000000000020561435240704200141020ustar00rootroot00000000000000MIT License Copyright (c) 2016 Florent Messa Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. go-funk-0.9.3/Makefile000066400000000000000000000001611435240704200145300ustar00rootroot00000000000000build: go build -v ./... test: go test -v ./... lint: golangci-lint run bench: go test -benchmem -bench . go-funk-0.9.3/README.rst000066400000000000000000000565661435240704200146030ustar00rootroot00000000000000go-funk ======= .. image:: https://secure.travis-ci.org/thoas/go-funk.svg?branch=master :alt: Build Status :target: http://travis-ci.org/thoas/go-funk .. image:: https://godoc.org/github.com/thoas/go-funk?status.svg :alt: GoDoc :target: https://pkg.go.dev/github.com/thoas/go-funk .. image:: https://goreportcard.com/badge/github.com/thoas/go-funk :alt: Go report :target: https://goreportcard.com/report/github.com/thoas/go-funk ``go-funk`` is a modern Go library based on reflect_. Generic helpers rely on reflect_, be careful this code runs exclusively on runtime so you must have a good test suite. These helpers have started as an experiment to learn reflect_. It may look like lodash_ in some aspects but it will have its own roadmap. lodash_ is an awesome library with a lot of work behind it, all features included in ``go-funk`` come from internal use cases. You can also find typesafe implementation in the godoc_. Why this name? -------------- Long story, short answer because ``func`` is a reserved word in Go, I wanted something similar. Initially this project was named ``fn`` I don't need to explain why that was a bad idea for french speakers :) Let's ``funk``! .. image:: https://media.giphy.com/media/3oEjHQKtDXpeGN9rW0/giphy.gif <3 Installation ------------ .. code-block:: bash go get github.com/thoas/go-funk Usage ----- .. code-block:: go import "github.com/thoas/go-funk" These examples will be based on the following data model: .. code-block:: go type Foo struct { ID int FirstName string `tag_name:"tag 1"` LastName string `tag_name:"tag 2"` Age int `tag_name:"tag 3"` } func (f Foo) TableName() string { return "foo" } With fixtures: .. code-block:: go f := &Foo{ ID: 1, FirstName: "Foo", LastName: "Bar", Age: 30, } You can import ``go-funk`` using a basic statement: .. code-block:: go import "github.com/thoas/go-funk" funk.Contains ............. Returns true if an element is present in a iteratee (slice, map, string). One frustrating thing in Go is to implement ``contains`` methods for each type, for example: .. code-block:: go func ContainsInt(s []int, e int) bool { for _, a := range s { if a == e { return true } } return false } this can be replaced by ``funk.Contains``: .. code-block:: go // slice of string funk.Contains([]string{"foo", "bar"}, "bar") // true // slice of Foo ptr funk.Contains([]*Foo{f}, f) // true funk.Contains([]*Foo{f}, func (foo *Foo) bool { return foo.ID == f.ID }) // true funk.Contains([]*Foo{f}, nil) // false b := &Foo{ ID: 2, FirstName: "Florent", LastName: "Messa", Age: 28, } funk.Contains([]*Foo{f}, b) // false // string funk.Contains("florent", "rent") // true funk.Contains("florent", "foo") // false // even map funk.Contains(map[int]string{1: "Florent"}, 1) // true funk.Contains(map[int]string{1: "Florent"}, func(key int, name string) bool { return key == 1 // or `name == "Florent"` for the value type }) // true see also, typesafe implementations: ContainsInt_, ContainsInt64_, ContainsFloat32_, ContainsFloat64_, ContainsString_ .. _ContainsFloat32: https://godoc.org/github.com/thoas/go-funk#ContainsFloat32 .. _ContainsFloat64: https://godoc.org/github.com/thoas/go-funk#ContainsFloat64 .. _ContainsInt: https://godoc.org/github.com/thoas/go-funk#ContainsInt .. _ContainsInt64: https://godoc.org/github.com/thoas/go-funk#ContainsInt64 .. _ContainsString: https://godoc.org/github.com/thoas/go-funk#ContainsString funk.Intersect .............. Returns the intersection between two collections. .. code-block:: go funk.Intersect([]int{1, 2, 3, 4}, []int{2, 4, 6}) // []int{2, 4} funk.Intersect([]string{"foo", "bar", "hello", "bar"}, []string{"foo", "bar"}) // []string{"foo", "bar"} see also, typesafe implementations: IntersectString .. IntersectString: https://godoc.org/github.com/thoas/go-funk#IntersectString funk.Difference .............. Returns the difference between two collections. .. code-block:: go funk.Difference([]int{1, 2, 3, 4}, []int{2, 4, 6}) // []int{1, 3}, []int{6} funk.Difference([]string{"foo", "bar", "hello", "bar"}, []string{"foo", "bar"}) // []string{"hello"}, []string{} see also, typesafe implementations: DifferenceString .. DifferenceString: https://godoc.org/github.com/thoas/go-funk#DifferenceString funk.IndexOf ............ Gets the index at which the first occurrence of a value is found in an array or return -1 if the value cannot be found. .. code-block:: go // slice of string funk.IndexOf([]string{"foo", "bar"}, "bar") // 1 funk.IndexOf([]string{"foo", "bar"}, func(value string) bool { return value == "bar" }) // 1 funk.IndexOf([]string{"foo", "bar"}, "gilles") // -1 see also, typesafe implementations: IndexOfInt_, IndexOfInt64_, IndexOfFloat32_, IndexOfFloat64_, IndexOfString_ .. _IndexOfFloat32: https://godoc.org/github.com/thoas/go-funk#IndexOfFloat32 .. _IndexOfFloat64: https://godoc.org/github.com/thoas/go-funk#IndexOfFloat64 .. _IndexOfInt: https://godoc.org/github.com/thoas/go-funk#IndexOfInt .. _IndexOfInt64: https://godoc.org/github.com/thoas/go-funk#IndexOfInt64 .. _IndexOfString: https://godoc.org/github.com/thoas/go-funk#IndexOfString funk.LastIndexOf ................ Gets the index at which the last occurrence of a value is found in an array or return -1 if the value cannot be found. .. code-block:: go // slice of string funk.LastIndexOf([]string{"foo", "bar", "bar"}, "bar") // 2 funk.LastIndexOf([]string{"foo", "bar"}, func(value string) bool { return value == "bar" }) // 2 funk.LastIndexOf([]string{"foo", "bar"}, "gilles") // -1 see also, typesafe implementations: LastIndexOfInt_, LastIndexOfInt64_, LastIndexOfFloat32_, LastIndexOfFloat64_, LastIndexOfString_ .. _LastIndexOfFloat32: https://godoc.org/github.com/thoas/go-funk#LastIndexOfFloat32 .. _LastIndexOfFloat64: https://godoc.org/github.com/thoas/go-funk#LastIndexOfFloat64 .. _LastIndexOfInt: https://godoc.org/github.com/thoas/go-funk#LastIndexOfInt .. _LastIndexOfInt64: https://godoc.org/github.com/thoas/go-funk#LastIndexOfInt64 .. _LastIndexOfString: https://godoc.org/github.com/thoas/go-funk#LastIndexOfString funk.ToMap .......... Transforms a slice or an array of structs to a map based on a ``pivot`` field. .. code-block:: go f := &Foo{ ID: 1, FirstName: "Gilles", LastName: "Fabio", Age: 70, } b := &Foo{ ID: 2, FirstName: "Florent", LastName: "Messa", Age: 80, } results := []*Foo{f, b} mapping := funk.ToMap(results, "ID") // map[int]*Foo{1: f, 2: b} funk.ToSet .......... Transforms an array or a slice to a set (a map with zero-size values). .. code-block:: go f := Foo{ ID: 1, FirstName: "Gilles", LastName: "Fabio", Age: 70, } b := Foo{ ID: 2, FirstName: "Florent", LastName: "Messa", Age: 80, } mapping := funk.ToSet([]Foo{f, b}) // map[Foo]stuct{}{f: struct{}{}, b: struct{}{}} mapping := funk.ToSet([4]int{1, 1, 2, 2}) // map[int]struct{}{1: struct{}{}, 2: struct{}{}} funk.Filter ........... Filters a slice based on a predicate. .. code-block:: go r := funk.Filter([]int{1, 2, 3, 4}, func(x int) bool { return x%2 == 0 }) // []int{2, 4} see also, typesafe implementations: FilterInt_, FilterInt64_, FilterFloat32_, FilterFloat64_, FilterString_ .. _FilterFloat32: https://godoc.org/github.com/thoas/go-funk#FilterFloat32 .. _FilterFloat64: https://godoc.org/github.com/thoas/go-funk#FilterFloat64 .. _FilterInt: https://godoc.org/github.com/thoas/go-funk#FilterInt .. _FilterInt64: https://godoc.org/github.com/thoas/go-funk#FilterInt64 .. _FilterString: https://godoc.org/github.com/thoas/go-funk#FilterString funk.Reduce ........... Reduces an iteratee based on an accumulator function or operation rune for numbers. .. code-block:: go // Using operation runes. '+' and '*' only supported. r := funk.Reduce([]int{1, 2, 3, 4}, '+', float64(0)) // 10 r := funk.Reduce([]int{1, 2, 3, 4}, '*', 1) // 24 // Using accumulator function r := funk.Reduce([]int{1, 2, 3, 4}, func(acc float64, num int) float64 { return acc + float64(num) }, float64(0)) // 10 r := funk.Reduce([]int{1, 2, 3, 4}, func(acc string, num int) string { return acc + fmt.Sprint(num) }, "") // "1234" funk.Find ......... Finds an element in a slice based on a predicate. .. code-block:: go r := funk.Find([]int{1, 2, 3, 4}, func(x int) bool { return x%2 == 0 }) // 2 see also, typesafe implementations: FindInt_, FindInt64_, FindFloat32_, FindFloat64_, FindString_ .. _FindFloat32: https://godoc.org/github.com/thoas/go-funk#FindFloat32 .. _FindFloat64: https://godoc.org/github.com/thoas/go-funk#FindFloat64 .. _FindInt: https://godoc.org/github.com/thoas/go-funk#FindInt .. _FindInt64: https://godoc.org/github.com/thoas/go-funk#FindInt64 .. _FindString: https://godoc.org/github.com/thoas/go-funk#FindString funk.Map ........ Manipulates an iteratee (map, slice) and transforms it to another type: * map -> slice * map -> map * slice -> map * slice -> slice .. code-block:: go r := funk.Map([]int{1, 2, 3, 4}, func(x int) int { return x * 2 }) // []int{2, 4, 6, 8} r := funk.Map([]int{1, 2, 3, 4}, func(x int) string { return "Hello" }) // []string{"Hello", "Hello", "Hello", "Hello"} r = funk.Map([]int{1, 2, 3, 4}, func(x int) (int, int) { return x, x }) // map[int]int{1: 1, 2: 2, 3: 3, 4: 4} mapping := map[int]string{ 1: "Florent", 2: "Gilles", } r = funk.Map(mapping, func(k int, v string) int { return k }) // []int{1, 2} r = funk.Map(mapping, func(k int, v string) (string, string) { return fmt.Sprintf("%d", k), v }) // map[string]string{"1": "Florent", "2": "Gilles"} funk.FlatMap ............ Manipulates an iteratee (map, slice) and transforms it to to a flattened collection of another type: * map -> slice * slice -> slice .. code-block:: go r := funk.FlatMap([][]int{{1, 2}, {3, 4}}, func(x []int) []int { return append(x, 0) }) // []int{1, 2, 0, 3, 4, 0} mapping := map[string][]int{ "Florent": {1, 2}, "Gilles": {3, 4}, } r = funk.FlatMap(mapping, func(k string, v []int) []int { return v }) // []int{1, 2, 3, 4} funk.Get ........ Retrieves the value at path of struct(s) or map(s). .. code-block:: go var bar *Bar = &Bar{ Name: "Test", Bars: []*Bar{ &Bar{ Name: "Level1-1", Bar: &Bar{ Name: "Level2-1", }, }, &Bar{ Name: "Level1-2", Bar: &Bar{ Name: "Level2-2", }, }, }, } var foo *Foo = &Foo{ ID: 1, FirstName: "Dark", LastName: "Vador", Age: 30, Bar: bar, Bars: []*Bar{ bar, bar, }, } funk.Get([]*Foo{foo}, "Bar.Bars.Bar.Name") // []string{"Level2-1", "Level2-2"} funk.Get(foo, "Bar.Bars.Bar.Name") // []string{"Level2-1", "Level2-2"} funk.Get(foo, "Bar.Name") // Test ``funk.Get`` also support ``map`` values: .. code-block:: go bar := map[string]interface{}{ "Name": "Test", } foo1 := map[string]interface{}{ "ID": 1, "FirstName": "Dark", "LastName": "Vador", "Age": 30, "Bar": bar, } foo2 := &map[string]interface{}{ "ID": 1, "FirstName": "Dark", "LastName": "Vador", "Age": 30, "Labels": map[string]interface{} { "example.com/hello": "world", }, } // foo2.Bar is nil funk.Get(bar, "Name") // "Test" funk.Get([]map[string]interface{}{foo1, foo2}, "Bar.Name") // []string{"Test"} funk.Get(foo2, "Bar.Name") // nil funk.Get(foo2, `Labels."example.com/hello"`) // world ``funk.Get`` also handles ``nil`` values: .. code-block:: go bar := &Bar{ Name: "Test", } foo1 := &Foo{ ID: 1, FirstName: "Dark", LastName: "Vador", Age: 30, Bar: bar, } foo2 := &Foo{ ID: 1, FirstName: "Dark", LastName: "Vador", Age: 30, } // foo2.Bar is nil funk.Get([]*Foo{foo1, foo2}, "Bar.Name") // []string{"Test"} funk.Get(foo2, "Bar.Name") // nil funk.GetOrElse .............. Retrieves the value of the pointer or default. .. code-block:: go str := "hello world" GetOrElse(&str, "foobar") // string{"hello world"} GetOrElse(str, "foobar") // string{"hello world"} GetOrElse(nil, "foobar") // string{"foobar"} funk.Set ........ Set value at a path of a struct .. code-block:: go var bar Bar = Bar{ Name: "level-0", Bar: &Bar{ Name: "level-1", Bars: []*Bar{ {Name: "level2-1"}, {Name: "level2-2"}, }, }, } _ = Set(&bar, "level-0-new", "Name") fmt.Println(bar.Name) // "level-0-new" MustSet(&bar, "level-1-new", "Bar.Name") fmt.Println(bar.Bar.Name) // "level-1-new" Set(&bar, "level-2-new", "Bar.Bars.Name") fmt.Println(bar.Bar.Bars[0].Name) // "level-2-new" fmt.Println(bar.Bar.Bars[1].Name) // "level-2-new" funk.MustSet ............ Short hand for funk.Set if struct does not contain interface{} field type to discard errors. funk.Prune .......... Copy a struct with only selected fields. Slice is handled by pruning all elements. .. code-block:: go bar := &Bar{ Name: "Test", } foo1 := &Foo{ ID: 1, FirstName: "Dark", LastName: "Vador", Bar: bar, } pruned, _ := Prune(foo1, []string{"FirstName", "Bar.Name"}) // *Foo{ // ID: 0, // FirstName: "Dark", // LastName: "", // Bar: &Bar{Name: "Test}, // } funk.PruneByTag .......... Same functionality as funk.Prune, but uses struct tags instead of struct field names. funk.Keys ......... Creates an array of the own enumerable map keys or struct field names. .. code-block:: go funk.Keys(map[string]int{"one": 1, "two": 2}) // []string{"one", "two"} (iteration order is not guaranteed) foo := &Foo{ ID: 1, FirstName: "Dark", LastName: "Vador", Age: 30, } funk.Keys(foo) // []string{"ID", "FirstName", "LastName", "Age"} (iteration order is not guaranteed) funk.Values ........... Creates an array of the own enumerable map values or struct field values. .. code-block:: go funk.Values(map[string]int{"one": 1, "two": 2}) // []int{1, 2} (iteration order is not guaranteed) foo := &Foo{ ID: 1, FirstName: "Dark", LastName: "Vador", Age: 30, } funk.Values(foo) // []interface{}{1, "Dark", "Vador", 30} (iteration order is not guaranteed) funk.ForEach ............ Range over an iteratee (map, slice). Or update element in slice(Not map, reflect#Value#MapIndex#CanSet is false). .. code-block:: go funk.ForEach([]int{1, 2, 3, 4}, func(x int) { fmt.Println(x) }) foo := []int{1,2,3} funk.ForEach(foo, func(x *int){ *x = *x * 2}) fmt.Println(foo) // []int{2, 4, 6} funk.ForEachRight ............ Range over an iteratee (map, slice) from the right. .. code-block:: go results := []int{} funk.ForEachRight([]int{1, 2, 3, 4}, func(x int) { results = append(results, x) }) fmt.Println(results) // []int{4, 3, 2, 1} funk.Chunk .......... Creates an array of elements split into groups with the length of the size. If array can't be split evenly, the final chunk will be the remaining element. .. code-block:: go funk.Chunk([]int{1, 2, 3, 4, 5}, 2) // [][]int{[]int{1, 2}, []int{3, 4}, []int{5}} funk.FlattenDeep ................ Recursively flattens an array. .. code-block:: go funk.FlattenDeep([][]int{[]int{1, 2}, []int{3, 4}}) // []int{1, 2, 3, 4} funk.Uniq ......... Creates an array with unique values. .. code-block:: go funk.Uniq([]int{0, 1, 1, 2, 3, 0, 0, 12}) // []int{0, 1, 2, 3, 12} see also, typesafe implementations: UniqInt_, UniqInt64_, UniqFloat32_, UniqFloat64_, UniqString_ .. _UniqFloat32: https://godoc.org/github.com/thoas/go-funk#UniqFloat32 .. _UniqFloat64: https://godoc.org/github.com/thoas/go-funk#UniqFloat64 .. _UniqInt: https://godoc.org/github.com/thoas/go-funk#UniqInt .. _UniqInt64: https://godoc.org/github.com/thoas/go-funk#UniqInt64 .. _UniqString: https://godoc.org/github.com/thoas/go-funk#UniqString funk.UniqBy ......... Creates an array with unique values returned by a callback. .. code-block:: go funk.UniqBy([]int{0, 1, 1, 2, 3, 0, 0, 12}, func(nbr int) int { return nbr % 3 }) // []int{0, 1, 2} foo1 := Foo{ ID: 42, FirstName: "Bob", } foo2 := Foo{ ID: 42, FirstName: "Bob", } funk.UniqBy([]Foo{foo1, foo2}, func(f Foo) int { return f.ID }) // []Foo{ Foo{ID: 42, Firstname: "Bob"} } funk.Drop ......... Creates an array/slice with `n` elements dropped from the beginning. .. code-block:: go funk.Drop([]int{0, 0, 0, 0}, 3) // []int{0} see also, typesafe implementations: DropInt_, DropInt32_, DropInt64_, DropFloat32_, DropFloat64_, DropString_ .. _DropInt: https://godoc.org/github.com/thoas/go-funk#DropInt .. _DropInt32: https://godoc.org/github.com/thoas/go-funk#DropInt64 .. _DropInt64: https://godoc.org/github.com/thoas/go-funk#DropInt64 .. _DropFloat32: https://godoc.org/github.com/thoas/go-funk#DropFloat32 .. _DropFloat64: https://godoc.org/github.com/thoas/go-funk#DropFloat64 .. _DropString: https://godoc.org/github.com/thoas/go-funk#DropString funk.Initial ............ Gets all but the last element of array. .. code-block:: go funk.Initial([]int{0, 1, 2, 3, 4}) // []int{0, 1, 2, 3} funk.Tail ......... Gets all but the first element of array. .. code-block:: go funk.Tail([]int{0, 1, 2, 3, 4}) // []int{1, 2, 3, 4} funk.Shuffle ............ Creates an array of shuffled values. .. code-block:: go funk.Shuffle([]int{0, 1, 2, 3, 4}) // []int{2, 1, 3, 4, 0} see also, typesafe implementations: ShuffleInt_, ShuffleInt64_, ShuffleFloat32_, ShuffleFloat64_, ShuffleString_ .. _ShuffleFloat32: https://godoc.org/github.com/thoas/go-funk#ShuffleFloat32 .. _ShuffleFloat64: https://godoc.org/github.com/thoas/go-funk#ShuffleFloat64 .. _ShuffleInt: https://godoc.org/github.com/thoas/go-funk#ShuffleInt .. _ShuffleInt64: https://godoc.org/github.com/thoas/go-funk#ShuffleInt64 .. _ShuffleString: https://godoc.org/github.com/thoas/go-funk#ShuffleString funk.Subtract ............. Returns the subtraction between two collections. It preserve order. .. code-block:: go funk.Subtract([]int{0, 1, 2, 3, 4}, []int{0, 4}) // []int{1, 2, 3} funk.Subtract([]int{0, 3, 2, 3, 4}, []int{0, 4}) // []int{3, 2, 3} see also, typesafe implementations: SubtractString_ .. SubtractString: https://godoc.org/github.com/thoas/go-funk#SubtractString funk.Sum ........ Computes the sum of the values in an array. .. code-block:: go funk.Sum([]int{0, 1, 2, 3, 4}) // 10.0 funk.Sum([]interface{}{0.5, 1, 2, 3, 4}) // 10.5 see also, typesafe implementations: SumInt_, SumInt64_, SumFloat32_, SumFloat64_ .. _SumFloat32: https://godoc.org/github.com/thoas/go-funk#SumFloat32 .. _SumFloat64: https://godoc.org/github.com/thoas/go-funk#SumFloat64 .. _SumInt: https://godoc.org/github.com/thoas/go-funk#SumInt .. _SumInt64: https://godoc.org/github.com/thoas/go-funk#SumInt64 funk.Reverse ............ Transforms an array such that the first element will become the last, the second element will become the second to last, etc. .. code-block:: go funk.Reverse([]int{0, 1, 2, 3, 4}) // []int{4, 3, 2, 1, 0} see also, typesafe implementations: ReverseInt_, ReverseInt64_, ReverseFloat32_, ReverseFloat64_, ReverseString_, ReverseStrings_ .. _ReverseFloat32: https://godoc.org/github.com/thoas/go-funk#ReverseFloat32 .. _ReverseFloat64: https://godoc.org/github.com/thoas/go-funk#ReverseFloat64 .. _ReverseInt: https://godoc.org/github.com/thoas/go-funk#ReverseInt .. _ReverseInt64: https://godoc.org/github.com/thoas/go-funk#ReverseInt64 .. _ReverseString: https://godoc.org/github.com/thoas/go-funk#ReverseString .. _ReverseStrings: https://godoc.org/github.com/thoas/go-funk#ReverseStrings funk.SliceOf ............ Returns a slice based on an element. .. code-block:: go funk.SliceOf(f) // will return a []*Foo{f} funk.RandomInt .............. Generates a random int, based on a min and max values. .. code-block:: go funk.RandomInt(0, 100) // will be between 0 and 100 funk.RandomString ................. Generates a random string with a fixed length. .. code-block:: go funk.RandomString(4) // will be a string of 4 random characters funk.Shard .......... Generates a sharded string with a fixed length and depth. .. code-block:: go funk.Shard("e89d66bdfdd4dd26b682cc77e23a86eb", 1, 2, false) // []string{"e", "8", "e89d66bdfdd4dd26b682cc77e23a86eb"} funk.Shard("e89d66bdfdd4dd26b682cc77e23a86eb", 2, 2, false) // []string{"e8", "9d", "e89d66bdfdd4dd26b682cc77e23a86eb"} funk.Shard("e89d66bdfdd4dd26b682cc77e23a86eb", 2, 3, true) // []string{"e8", "9d", "66", "bdfdd4dd26b682cc77e23a86eb"} funk.Subset ............. Returns true if a collection is a subset of another .. code-block:: go funk.Subset([]int{1, 2, 4}, []int{1, 2, 3, 4, 5}) // true funk.Subset([]string{"foo", "bar"},[]string{"foo", "bar", "hello", "bar", "hi"}) //true Performance ----------- ``go-funk`` currently has an open issue about performance_, don't hesitate to participate in the discussion to enhance the generic helpers implementations. Let's stop beating around the bush, a typesafe implementation in pure Go of ``funk.Contains``, let's say for example: .. code-block:: go func ContainsInt(s []int, e int) bool { for _, a := range s { if a == e { return true } } return false } will always outperform an implementation based on reflect_ in terms of speed and allocs because of how it's implemented in the language. If you want a similarity, gorm_ will always be slower than sqlx_ (which is very low level btw) and will use more allocs. You must not think generic helpers of ``go-funk`` as a replacement when you are dealing with performance in your codebase, you should use typesafe implementations instead. Contributing ------------ * Ping me on twitter `@thoas `_ (DMs, mentions, whatever :)) * Fork the `project `_ * Fix `open issues `_ or request new features Don't hesitate ;) Authors ------- * Florent Messa * Gilles Fabio * Alexey Pokhozhaev * Alexandre Nicolaie .. _reflect: https://golang.org/pkg/reflect/ .. _lodash: https://lodash.com/ .. _performance: https://github.com/thoas/go-funk/issues/19 .. _gorm: https://github.com/jinzhu/gorm .. _sqlx: https://github.com/jmoiron/sqlx .. _godoc: https://godoc.org/github.com/thoas/go-funk go-funk-0.9.3/assign.go000066400000000000000000000105501435240704200147060ustar00rootroot00000000000000package funk import ( "errors" "fmt" "reflect" "strings" ) // Set assigns in at path with value val. i.e. in.path = val // in accepts types of ptr to struct, ptr to variable, slice and ptr to slice. // Along the path, interface{} is supported and nil ptr is initialized to ptr to zero value // of the type until the variable to be set is obtained. // It returns errors when encountering along the path unknown types, uninitialized // interface{} or interface{} containing struct directly (not ptr to struct). // // Slice is resolved the same way in funk.Get(), by traversing each element of the slice, // so that each element of the slice's corresponding field are going to be set to the same provided val. // If Set is called on slice with empty path "", it behaves the same as funk.Fill() // // If in is well formed, i.e. do not expect above descripted errors to happen, funk.MustSet() // is a short hand wrapper to discard error return func Set(in interface{}, val interface{}, path string) error { if in == nil { return errors.New("Cannot Set nil") } parts := []string{} if path != "" { parts = strings.Split(path, ".") } return setByParts(in, val, parts) } // we need this layer to handle interface{} type func setByParts(in interface{}, val interface{}, parts []string) error { if in == nil { // nil interface can happen during traversing the path return errors.New("Cannot traverse nil/uninitialized interface{}") } inValue := reflect.ValueOf(in) inKind := inValue.Type().Kind() // Note: if interface contains a struct (not ptr to struct) then the content of the struct cannot be set. // I.e. it is not CanAddr() or CanSet() // So we require in interface{} to be a ptr, slice or array if inKind == reflect.Ptr { inValue = inValue.Elem() // if it is ptr we set its content not ptr its self } else if inKind != reflect.Array && inKind != reflect.Slice { return fmt.Errorf("Type %s not supported by Set", inValue.Type().String()) } return set(inValue, reflect.ValueOf(val), parts) } // traverse inValue using path in parts and set the dst to be setValue func set(inValue reflect.Value, setValue reflect.Value, parts []string) error { // traverse the path to get the inValue we need to set i := 0 for i < len(parts) { kind := inValue.Kind() switch kind { case reflect.Invalid: // do not expect this case to happen return errors.New("nil pointer found along the path") case reflect.Struct: fValue := inValue.FieldByName(parts[i]) if !fValue.IsValid() { return fmt.Errorf("field name %v is not found in struct %v", parts[i], inValue.Type().String()) } if !fValue.CanSet() { return fmt.Errorf("field name %v is not exported in struct %v", parts[i], inValue.Type().String()) } inValue = fValue i++ case reflect.Slice, reflect.Array: // set all its elements length := inValue.Len() for j := 0; j < length; j++ { err := set(inValue.Index(j), setValue, parts[i:]) if err != nil { return err } } return nil case reflect.Ptr: // only traverse down one level if inValue.IsNil() { // we initialize nil ptr to ptr to zero value of the type // and continue traversing inValue.Set(reflect.New(inValue.Type().Elem())) } // traverse the ptr until it is not pointer any more or is nil again inValue = redirectValue(inValue) case reflect.Interface: // Note: if interface contains a struct (not ptr to struct) then the content of the struct cannot be set. // I.e. it is not CanAddr() or CanSet(). This is why setByParts has a nil ptr check. // we treat this as a new call to setByParts, and it will do proper check of the types return setByParts(inValue.Interface(), setValue.Interface(), parts[i:]) default: return fmt.Errorf("kind %v in path %v is not supported", kind, parts[i]) } } // here inValue holds the value we need to set // interface{} can be set to any val // other types we ensure the type matches if inValue.Kind() != setValue.Kind() && inValue.Kind() != reflect.Interface { return fmt.Errorf("cannot set target of type %v with type %v", inValue.Kind(), setValue.Kind()) } inValue.Set(setValue) return nil } // MustSet is functionally the same as Set. // It panics instead of returning error. // It is safe to use if the in value is well formed. func MustSet(in interface{}, val interface{}, path string) { err := Set(in, val, path) if err != nil { panic(err) } } go-funk-0.9.3/assign_test.go000066400000000000000000000246661435240704200157620ustar00rootroot00000000000000package funk import ( "database/sql" "fmt" "testing" "github.com/stretchr/testify/assert" ) func TestSet_EmptyPath(t *testing.T) { // it is supposed to change the var passed in var testCases = []struct { // will use path = "" Original interface{} SetVal interface{} }{ // int { Original: 100, SetVal: 1, }, // string { Original: "", SetVal: "val", }, // struct { Original: Bar{Name: "bar"}, SetVal: Bar{Name: "val"}, }, // slice { Original: []Bar{{Name: "bar"}}, SetVal: []Bar{{Name: "val1"}, {Name: "val2"}}, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) // use empty path // must take the addr of the variable to be set err := Set(&tc.Original, tc.SetVal, "") is.NoError(err) is.Equal(tc.Original, tc.SetVal) // original should be set to SetVal }) } } func TestSet_StructBasicOneLevel(t *testing.T) { is := assert.New(t) // we set field one by one of baz with expected value baz := Foo{ ID: 100, FirstName: "firstname", LastName: "lastname", Age: 23, Bar: &Bar{Name: "bar"}, Bars: []*Bar{{Name: "1"}}, EmptyValue: sql.NullInt64{ Int64: 64, Valid: false, }, } expected := Foo{ ID: 1, FirstName: "firstname1", LastName: "lastname1", Age: 24, Bar: &Bar{Name: "b1", Bar: &Bar{Name: "b2"}}, Bars: []*Bar{{Name: "1"}, {Name: "2"}}, EmptyValue: sql.NullInt64{ Int64: 11, Valid: true, }, } err := Set(&baz, 1, "ID") is.NoError(err) err = Set(&baz, expected.FirstName, "FirstName") is.NoError(err) err = Set(&baz, expected.LastName, "LastName") is.NoError(err) err = Set(&baz, expected.Age, "Age") is.NoError(err) err = Set(&baz, expected.Bar, "Bar") is.NoError(err) err = Set(&baz, expected.Bars, "Bars") is.NoError(err) err = Set(&baz, expected.EmptyValue, "EmptyValue") is.NoError(err) is.Equal(baz, expected) } func TestSetStruct_MultiLevels(t *testing.T) { var testCases = []struct { Original Bar Path string SetVal interface{} Expected Bar }{ // Set slice in 4th level { Original: Bar{ Name: "1", // name indicates level Bar: &Bar{ Name: "2", Bars: []*Bar{ {Name: "3-1", Bars: []*Bar{{Name: "4-1"}, {Name: "4-2"}, {Name: "4-3"}}}, {Name: "3-2", Bars: []*Bar{{Name: "4-1"}, {Name: "4-2"}}}, }, }, }, Path: "Bar.Bars.Bars.Name", SetVal: "val", Expected: Bar{ Name: "1", Bar: &Bar{ Name: "2", Bars: []*Bar{ {Name: "3-1", Bars: []*Bar{{Name: "val"}, {Name: "val"}, {Name: "val"}}}, {Name: "3-2", Bars: []*Bar{{Name: "val"}, {Name: "val"}}}, }, }, }, }, // Set multilevel uninitialized ptr { Original: Bar{ Name: "1", // name indicates level Bar: nil, }, Path: "Bar.Bar.Bar.Name", SetVal: "val", Expected: Bar{ Name: "1", Bar: &Bar{ Name: "", // level 2 Bar: &Bar{ Bar: &Bar{ Name: "val", //level 3 }, }, }, }, }, // mix of uninitialized ptr and slices { Original: Bar{ Name: "1", // name indicates level Bar: &Bar{ Name: "2", Bars: []*Bar{ {Name: "3-1", Bars: []*Bar{{Name: "4-1"}, {Name: "4-2"}, {Name: "4-3"}}}, {Name: "3-2", Bars: []*Bar{{Name: "4-1"}, {Name: "4-2"}}}, }, }, }, Path: "Bar.Bars.Bars.Bar.Name", SetVal: "val", Expected: Bar{ Name: "1", // name indicates level Bar: &Bar{ Name: "2", Bars: []*Bar{ {Name: "3-1", Bars: []*Bar{{Name: "4-1", Bar: &Bar{Name: "val"}}, {Name: "4-2", Bar: &Bar{Name: "val"}}, {Name: "4-3", Bar: &Bar{Name: "val"}}}}, {Name: "3-2", Bars: []*Bar{{Name: "4-1", Bar: &Bar{Name: "val"}}, {Name: "4-2", Bar: &Bar{Name: "val"}}}}, }, }, }, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) // take the addr and then pass it in err := Set(&tc.Original, tc.SetVal, tc.Path) is.NoError(err) is.Equal(tc.Expected, tc.Original) }) } } func TestSet_StructWithCyclicStruct(t *testing.T) { is := assert.New(t) testBar := Bar{ Name: "testBar", Bar: nil, } testBar.Bar = &testBar err := Set(&testBar, "val", "Bar.Bar.Name") is.NoError(err) is.Equal("val", testBar.Name) } func TestSet_StructWithFieldNotInitialized(t *testing.T) { is := assert.New(t) myFoo := &Foo{ Bar: nil, // we will try to set bar's field } err := Set(myFoo, "name", "Bar.Name") is.NoError(err) is.Equal("name", myFoo.Bar.Name) } func TestSet_SlicePassByPtr(t *testing.T) { var testCases = []struct { Original interface{} // slice or array Path string SetVal interface{} Expected interface{} }{ // Set Slice itself { Original: []*Bar{}, Path: "", // empty path means set the passed in ptr itself SetVal: []*Bar{{Name: "bar"}}, Expected: []*Bar{{Name: "bar"}}, }, // empty slice { Original: []*Bar{}, Path: "Name", SetVal: "val", Expected: []*Bar{}, }, // slice of ptr { Original: []*Bar{{Name: "a"}, {Name: "b"}}, Path: "Name", SetVal: "val", Expected: []*Bar{{Name: "val"}, {Name: "val"}}, }, // slice of struct { Original: []Bar{{Name: "a"}, {Name: "b"}}, Path: "Name", SetVal: "val", Expected: []Bar{{Name: "val"}, {Name: "val"}}, }, // slice of empty ptr { Original: []*Bar{nil, nil}, Path: "Name", SetVal: "val", Expected: []*Bar{{Name: "val"}, {Name: "val"}}, }, // mix of init ptr and nil ptr { Original: []*Bar{{Name: "bar"}, nil}, Path: "Name", SetVal: "val", Expected: []*Bar{{Name: "val"}, {Name: "val"}}, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) // take the addr and then pass it in err := Set(&tc.Original, tc.SetVal, tc.Path) is.NoError(err) is.Equal(tc.Expected, tc.Original) }) } } func TestSet_SlicePassDirectly(t *testing.T) { var testCases = []struct { Original interface{} // slice or array Path string SetVal interface{} Expected interface{} }{ // Set Slice itself does not work here since not passing by ptr // empty slice { Original: []*Bar{}, Path: "Name", SetVal: "val", Expected: []*Bar{}, }, // slice of ptr { Original: []*Bar{{Name: "a"}, {Name: "b"}}, Path: "Name", SetVal: "val", Expected: []*Bar{{Name: "val"}, {Name: "val"}}, }, // Array of ptr { Original: [2]*Bar{{Name: "a"}, {Name: "b"}}, Path: "Name", SetVal: "val", Expected: [2]*Bar{{Name: "val"}, {Name: "val"}}, }, // slice of struct { Original: []Bar{{Name: "a"}, {Name: "b"}}, Path: "Name", SetVal: "val", Expected: []Bar{{Name: "val"}, {Name: "val"}}, }, // slice of empty ptr { Original: []*Bar{nil, nil}, Path: "Name", SetVal: "val", Expected: []*Bar{{Name: "val"}, {Name: "val"}}, }, // mix of init ptr and nil ptr { Original: []*Bar{{Name: "bar"}, nil}, Path: "Name", SetVal: "val", Expected: []*Bar{{Name: "val"}, {Name: "val"}}, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) // Not take ptr, pass directly err := Set(tc.Original, tc.SetVal, tc.Path) is.NoError(err) is.Equal(tc.Expected, tc.Original) }) } } func TestInterface(t *testing.T) { var testCases = []struct { OriginalFoo Foo Path string SetVal interface{} ExpectedFoo Foo }{ // set string field { Foo{FirstName: ""}, "FirstName", "hi", Foo{FirstName: "hi"}, }, // set interface{} field { Foo{FirstName: "", GeneralInterface: nil}, "GeneralInterface", "str", Foo{FirstName: "", GeneralInterface: "str"}, }, // set field of the interface{} field // Note: set uninitialized interface{} should fail // Note: interface of struct (not ptr to struct) should fail { Foo{FirstName: "", GeneralInterface: &Foo{FirstName: ""}}, // if Foo is not ptr this will fail "GeneralInterface.FirstName", "foo", Foo{FirstName: "", GeneralInterface: &Foo{FirstName: "foo"}}, }, // interface two level { Foo{FirstName: "", GeneralInterface: &Foo{GeneralInterface: nil}}, "GeneralInterface.GeneralInterface", "val", Foo{FirstName: "", GeneralInterface: &Foo{GeneralInterface: "val"}}, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) err := Set(&tc.OriginalFoo, tc.SetVal, tc.Path) is.NoError(err) is.Equal(tc.ExpectedFoo, tc.OriginalFoo) }) } } func TestSet_ErrorCaces(t *testing.T) { var testCases = []struct { OriginalFoo Foo Path string SetVal interface{} }{ // uninit interface // Itf is not initialized so Set cannot properly allocate type { Foo{BarInterface: nil}, "BarInterface.Name", "val", }, { Foo{GeneralInterface: &Foo{BarInterface: nil}}, "GeneralInterface.BarInterface.Name", "val", }, // type mismatch { Foo{FirstName: ""}, "FirstName", 20, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) err := Set(&tc.OriginalFoo, tc.SetVal, tc.Path) is.Error(err) }) } t.Run("not pointer", func(t *testing.T) { is := assert.New(t) baz := Bar{Name: "dummy"} err := Set(baz, Bar{Name: "dummy2"}, "Name") is.Error(err) }) t.Run("Unexported field", func(t *testing.T) { is := assert.New(t) s := struct { name string }{name: "dummy"} err := Set(&s, s, "name") is.Error(err) }) } func TestMustSet_Basic(t *testing.T) { t.Run("Variable", func(t *testing.T) { is := assert.New(t) s := 1 MustSet(&s, 2, "") is.Equal(2, s) }) t.Run("Struct", func(t *testing.T) { is := assert.New(t) s := Bar{Name: "a"} MustSet(&s, "b", "Name") is.Equal("b", s.Name) }) } // Examples func ExampleSet() { var bar Bar = Bar{ Name: "level-0", Bar: &Bar{ Name: "level-1", Bars: []*Bar{ {Name: "level2-1"}, {Name: "level2-2"}, }, }, } _ = Set(&bar, "level-0-new", "Name") fmt.Println(bar.Name) // discard error use MustSet MustSet(&bar, "level-1-new", "Bar.Name") fmt.Println(bar.Bar.Name) _ = Set(&bar, "level-2-new", "Bar.Bars.Name") fmt.Println(bar.Bar.Bars[0].Name) fmt.Println(bar.Bar.Bars[1].Name) // Output: // level-0-new // level-1-new // level-2-new // level-2-new } go-funk-0.9.3/benchmark_test.go000066400000000000000000000044711435240704200164200ustar00rootroot00000000000000package funk import ( "math/rand" "testing" ) const ( seed = 918234565 sliceSize = 3614562 ) func sliceGenerator(size uint, r *rand.Rand) (out []int64) { for i := uint(0); i < size; i++ { out = append(out, rand.Int63()) } return } func BenchmarkSubtract(b *testing.B) { r := rand.New(rand.NewSource(seed)) testData := sliceGenerator(sliceSize, r) what := sliceGenerator(sliceSize, r) b.Run("Subtract", func(b *testing.B) { for n := 0; n < b.N; n++ { Subtract(testData, what) } }) } func BenchmarkContains(b *testing.B) { r := rand.New(rand.NewSource(seed)) testData := sliceGenerator(sliceSize, r) what := r.Int63() b.Run("ContainsInt64", func(b *testing.B) { for n := 0; n < b.N; n++ { ContainsInt64(testData, what) } }) b.Run("IndexOfInt64", func(b *testing.B) { for n := 0; n < b.N; n++ { IndexOfInt64(testData, what) } }) b.Run("Contains", func(b *testing.B) { for n := 0; n < b.N; n++ { Contains(testData, what) } }) } func BenchmarkUniq(b *testing.B) { r := rand.New(rand.NewSource(seed)) testData := sliceGenerator(sliceSize, r) b.Run("UniqInt64", func(b *testing.B) { for n := 0; n < b.N; n++ { UniqInt64(testData) } }) b.Run("Uniq", func(b *testing.B) { for n := 0; n < b.N; n++ { Uniq(testData) } }) } func BenchmarkSum(b *testing.B) { r := rand.New(rand.NewSource(seed)) testData := sliceGenerator(sliceSize, r) b.Run("SumInt64", func(b *testing.B) { for n := 0; n < b.N; n++ { SumInt64(testData) } }) b.Run("Sum", func(b *testing.B) { for n := 0; n < b.N; n++ { Sum(testData) } }) } func BenchmarkDrop(b *testing.B) { r := rand.New(rand.NewSource(seed)) testData := sliceGenerator(sliceSize, r) b.Run("DropInt64", func(b *testing.B) { for n := 0; n < b.N; n++ { DropInt64(testData, 1) } }) b.Run("Drop", func(b *testing.B) { for n := 0; n < b.N; n++ { Drop(testData, 1) } }) } func BenchmarkJoin(b *testing.B) { r := rand.New(rand.NewSource(seed)) fullArr := sliceGenerator(sliceSize, r) leftArr := fullArr[:sliceSize/3*2] rightArr := fullArr[sliceSize/3*1:] b.Run("InnerJoinInt64", func(b *testing.B) { for n := 0; n < b.N; n++ { JoinInt64(leftArr, rightArr, InnerJoinInt64) } }) b.Run("InnerJoin", func(b *testing.B) { for n := 0; n < b.N; n++ { Join(leftArr, rightArr, InnerJoin) } }) } go-funk-0.9.3/builder.go000066400000000000000000000064251435240704200150560ustar00rootroot00000000000000package funk import ( "fmt" "reflect" ) // Builder contains all tools which can be chained. type Builder interface { Chunk(size int) Builder Compact() Builder Drop(n int) Builder Filter(predicate interface{}) Builder Flatten() Builder FlattenDeep() Builder Initial() Builder Intersect(y interface{}) Builder Join(rarr interface{}, fnc JoinFnc) Builder Map(mapFunc interface{}) Builder FlatMap(mapFunc interface{}) Builder Reverse() Builder Shuffle() Builder Tail() Builder Uniq() Builder Without(values ...interface{}) Builder All() bool Any() bool Contains(elem interface{}) bool Every(elements ...interface{}) bool Find(predicate interface{}) interface{} ForEach(predicate interface{}) ForEachRight(predicate interface{}) Head() interface{} Keys() interface{} IndexOf(elem interface{}) int IsEmpty() bool Last() interface{} LastIndexOf(elem interface{}) int NotEmpty() bool Product() float64 Reduce(reduceFunc, acc interface{}) interface{} Sum() float64 Type() reflect.Type Value() interface{} Values() interface{} } // Chain creates a simple new go-funk.Builder from a collection. Each method // call generate a new builder containing the previous result. func Chain(v interface{}) Builder { isNotNil(v, "Chain") valueType := reflect.TypeOf(v) if isValidBuilderEntry(valueType) || (valueType.Kind() == reflect.Ptr && isValidBuilderEntry(valueType.Elem())) { return &chainBuilder{v} } panic(fmt.Sprintf("Type %s is not supported by Chain", valueType.String())) } // LazyChain creates a lazy go-funk.Builder from a collection. Each method call // generate a new builder containing a method generating the previous value. // With that, all data are only generated when we call a tailling method like All or Find. func LazyChain(v interface{}) Builder { isNotNil(v, "LazyChain") valueType := reflect.TypeOf(v) if isValidBuilderEntry(valueType) || (valueType.Kind() == reflect.Ptr && isValidBuilderEntry(valueType.Elem())) { return &lazyBuilder{func() interface{} { return v }} } panic(fmt.Sprintf("Type %s is not supported by LazyChain", valueType.String())) } // LazyChainWith creates a lazy go-funk.Builder from a generator. Like LazyChain, each // method call generate a new builder containing a method generating the previous value. // But, instead of using a collection, it takes a generator which can generate values. // With LazyChainWith, to can create a generic pipeline of collection transformation and, // throw the generator, sending different collection. func LazyChainWith(generator func() interface{}) Builder { isNotNil(generator, "LazyChainWith") return &lazyBuilder{func() interface{} { isNotNil(generator, "LazyChainWith") v := generator() valueType := reflect.TypeOf(v) if isValidBuilderEntry(valueType) || (valueType.Kind() == reflect.Ptr && isValidBuilderEntry(valueType.Elem())) { return v } panic(fmt.Sprintf("Type %s is not supported by LazyChainWith generator", valueType.String())) }} } func isNotNil(v interface{}, from string) { if v == nil { panic(fmt.Sprintf("nil value is not supported by %s", from)) } } func isValidBuilderEntry(valueType reflect.Type) bool { return valueType.Kind() == reflect.Slice || valueType.Kind() == reflect.Array || valueType.Kind() == reflect.Map || valueType.Kind() == reflect.String } go-funk-0.9.3/builder_test.go000066400000000000000000000131071435240704200161100ustar00rootroot00000000000000package funk import ( "fmt" "testing" "github.com/stretchr/testify/assert" ) func TestChain(t *testing.T) { testCases := []struct { In interface{} Panic string }{ // Check with array types {In: []int{0, 1, 2}}, {In: []string{"aaa", "bbb", "ccc"}}, {In: []interface{}{0, false, "___"}}, // Check with map types {In: map[int]string{0: "aaa", 1: "bbb", 2: "ccc"}}, {In: map[string]string{"0": "aaa", "1": "bbb", "2": "ccc"}}, {In: map[int]interface{}{0: 0, 1: false, 2: "___"}}, // Check with invalid types {false, "Type bool is not supported by Chain"}, {0, "Type int is not supported by Chain"}, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) if tc.Panic != "" { is.PanicsWithValue(tc.Panic, func() { Chain(tc.In) }) return } chain := Chain(tc.In) collection := chain.(*chainBuilder).collection is.Equal(collection, tc.In) }) } } func TestLazyChain(t *testing.T) { testCases := []struct { In interface{} Panic string }{ // Check with array types {In: []int{0, 1, 2}}, {In: []string{"aaa", "bbb", "ccc"}}, {In: []interface{}{0, false, "___"}}, // Check with map types {In: map[int]string{0: "aaa", 1: "bbb", 2: "ccc"}}, {In: map[string]string{"0": "aaa", "1": "bbb", "2": "ccc"}}, {In: map[int]interface{}{0: 0, 1: false, 2: "___"}}, // Check with invalid types {false, "Type bool is not supported by LazyChain"}, {0, "Type int is not supported by LazyChain"}, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) if tc.Panic != "" { is.PanicsWithValue(tc.Panic, func() { LazyChain(tc.In) }) return } chain := LazyChain(tc.In) collection := chain.(*lazyBuilder).exec() is.Equal(collection, tc.In) }) } } func TestLazyChainWith(t *testing.T) { testCases := []struct { In func() interface{} Panic string }{ // Check with array types {In: func() interface{} { return []int{0, 1, 2} }}, {In: func() interface{} { return []string{"aaa", "bbb", "ccc"} }}, {In: func() interface{} { return []interface{}{0, false, "___"} }}, // Check with map types {In: func() interface{} { return map[int]string{0: "aaa", 1: "bbb", 2: "ccc"} }}, {In: func() interface{} { return map[string]string{"0": "aaa", "1": "bbb", "2": "ccc"} }}, {In: func() interface{} { return map[int]interface{}{0: 0, 1: false, 2: "___"} }}, // Check with invalid types { In: func() interface{} { return false }, Panic: "Type bool is not supported by LazyChainWith generator", }, { In: func() interface{} { return 0 }, Panic: "Type int is not supported by LazyChainWith generator", }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) if tc.Panic != "" { is.PanicsWithValue(tc.Panic, func() { LazyChainWith(tc.In).(*lazyBuilder).exec() }) return } chain := LazyChainWith(tc.In) collection := chain.(*lazyBuilder).exec() is.Equal(collection, tc.In()) }) } } func ExampleChain() { v := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} chain := Chain(v) lazy := LazyChain(v) // Without builder a := Filter(v, func(x int) bool { return x%2 == 0 }) b := Map(a, func(x int) int { return x * 2 }) c := Reverse(a) fmt.Printf("funk.Contains(b, 2): %v\n", Contains(b, 2)) // false fmt.Printf("funk.Contains(b, 4): %v\n", Contains(b, 4)) // true fmt.Printf("funk.Sum(b): %v\n", Sum(b)) // 40 fmt.Printf("funk.Head(b): %v\n", Head(b)) // 4 fmt.Printf("funk.Head(c): %v\n\n", Head(c)) // 8 // With simple chain builder ca := chain.Filter(func(x int) bool { return x%2 == 0 }) cb := ca.Map(func(x int) int { return x * 2 }) cc := ca.Reverse() fmt.Printf("chainB.Contains(2): %v\n", cb.Contains(2)) // false fmt.Printf("chainB.Contains(4): %v\n", cb.Contains(4)) // true fmt.Printf("chainB.Sum(): %v\n", cb.Sum()) // 40 fmt.Printf("chainB.Head(): %v\n", cb.Head()) // 4 fmt.Printf("chainC.Head(): %v\n\n", cc.Head()) // 8 // With lazy chain builder la := lazy.Filter(func(x int) bool { return x%2 == 0 }) lb := la.Map(func(x int) int { return x * 2 }) lc := la.Reverse() fmt.Printf("lazyChainB.Contains(2): %v\n", lb.Contains(2)) // false fmt.Printf("lazyChainB.Contains(4): %v\n", lb.Contains(4)) // true fmt.Printf("lazyChainB.Sum(): %v\n", lb.Sum()) // 40 fmt.Printf("lazyChainB.Head(): %v\n", lb.Head()) // 4 fmt.Printf("lazyChainC.Head(): %v\n", lc.Head()) // 8 } type updatingStruct struct { x []int } func (us *updatingStruct) Values() interface{} { return us.x } func ExampleLazyChain() { us := updatingStruct{} chain := Chain(us.x). Map(func(x int) float64 { return float64(x) * 2.5 }) lazy := LazyChain(us.x). Map(func(x int) float64 { return float64(x) * 2.5 }) lazyWith := LazyChainWith(us.Values). Map(func(x int) float64 { return float64(x) * 2.5 }) fmt.Printf("chain.Sum(): %v\n", chain.Sum()) // 0 fmt.Printf("lazy.Sum(): %v\n", lazy.Sum()) // 0 fmt.Printf("lazyWith.Sum(): %v\n\n", lazyWith.Sum()) // 0 us.x = append(us.x, 2) fmt.Printf("chain.Sum(): %v\n", chain.Sum()) // 0 fmt.Printf("lazy.Sum(): %v\n", lazy.Sum()) // 0 fmt.Printf("lazyWith.Sum(): %v\n\n", lazyWith.Sum()) // 5 us.x = append(us.x, 10) fmt.Printf("chain.Sum(): %v\n", chain.Sum()) // 0 fmt.Printf("lazy.Sum(): %v\n", lazy.Sum()) // 0 fmt.Printf("lazyWith.Sum(): %v\n\n", lazyWith.Sum()) // 30 } go-funk-0.9.3/chain_builder.go000066400000000000000000000075671435240704200162300ustar00rootroot00000000000000package funk import ( "fmt" "reflect" ) type chainBuilder struct { collection interface{} } func (b *chainBuilder) Chunk(size int) Builder { return &chainBuilder{Chunk(b.collection, size)} } func (b *chainBuilder) Compact() Builder { return &chainBuilder{Compact(b.collection)} } func (b *chainBuilder) Drop(n int) Builder { return &chainBuilder{Drop(b.collection, n)} } func (b *chainBuilder) Filter(predicate interface{}) Builder { return &chainBuilder{Filter(b.collection, predicate)} } func (b *chainBuilder) Flatten() Builder { return &chainBuilder{Flatten(b.collection)} } func (b *chainBuilder) FlattenDeep() Builder { return &chainBuilder{FlattenDeep(b.collection)} } func (b *chainBuilder) Initial() Builder { return &chainBuilder{Initial(b.collection)} } func (b *chainBuilder) Intersect(y interface{}) Builder { return &chainBuilder{Intersect(b.collection, y)} } func (b *chainBuilder) Join(rarr interface{}, fnc JoinFnc) Builder { return &chainBuilder{Join(b.collection, rarr, fnc)} } func (b *chainBuilder) Map(mapFunc interface{}) Builder { return &chainBuilder{Map(b.collection, mapFunc)} } func (b *chainBuilder) FlatMap(mapFunc interface{}) Builder { return &chainBuilder{FlatMap(b.collection, mapFunc)} } func (b *chainBuilder) Reverse() Builder { return &chainBuilder{Reverse(b.collection)} } func (b *chainBuilder) Shuffle() Builder { return &chainBuilder{Shuffle(b.collection)} } func (b *chainBuilder) Tail() Builder { return &chainBuilder{Tail(b.collection)} } func (b *chainBuilder) Uniq() Builder { return &chainBuilder{Uniq(b.collection)} } func (b *chainBuilder) Without(values ...interface{}) Builder { return &chainBuilder{Without(b.collection, values...)} } func (b *chainBuilder) All() bool { v := reflect.ValueOf(b.collection) t := v.Type() if t.Kind() != reflect.Array && t.Kind() != reflect.Slice { panic(fmt.Sprintf("Type %s is not supported by Chain.All", t.String())) } c := make([]interface{}, v.Len()) for i := 0; i < v.Len(); i++ { c[i] = v.Index(i).Interface() } return All(c...) } func (b *chainBuilder) Any() bool { v := reflect.ValueOf(b.collection) t := v.Type() if t.Kind() != reflect.Array && t.Kind() != reflect.Slice { panic(fmt.Sprintf("Type %s is not supported by Chain.Any", t.String())) } c := make([]interface{}, v.Len()) for i := 0; i < v.Len(); i++ { c[i] = v.Index(i).Interface() } return Any(c...) } func (b *chainBuilder) Contains(elem interface{}) bool { return Contains(b.collection, elem) } func (b *chainBuilder) Every(elements ...interface{}) bool { return Every(b.collection, elements...) } func (b *chainBuilder) Find(predicate interface{}) interface{} { return Find(b.collection, predicate) } func (b *chainBuilder) ForEach(predicate interface{}) { ForEach(b.collection, predicate) } func (b *chainBuilder) ForEachRight(predicate interface{}) { ForEachRight(b.collection, predicate) } func (b *chainBuilder) Head() interface{} { return Head(b.collection) } func (b *chainBuilder) Keys() interface{} { return Keys(b.collection) } func (b *chainBuilder) IndexOf(elem interface{}) int { return IndexOf(b.collection, elem) } func (b *chainBuilder) IsEmpty() bool { return IsEmpty(b.collection) } func (b *chainBuilder) Last() interface{} { return Last(b.collection) } func (b *chainBuilder) LastIndexOf(elem interface{}) int { return LastIndexOf(b.collection, elem) } func (b *chainBuilder) NotEmpty() bool { return NotEmpty(b.collection) } func (b *chainBuilder) Product() float64 { return Product(b.collection) } func (b *chainBuilder) Reduce(reduceFunc, acc interface{}) interface{} { return Reduce(b.collection, reduceFunc, acc) } func (b *chainBuilder) Sum() float64 { return Sum(b.collection) } func (b *chainBuilder) Type() reflect.Type { return reflect.TypeOf(b.collection) } func (b *chainBuilder) Value() interface{} { return b.collection } func (b *chainBuilder) Values() interface{} { return Values(b.collection) } go-funk-0.9.3/chain_builder_test.go000066400000000000000000000566371435240704200172710ustar00rootroot00000000000000package funk import ( "fmt" "reflect" "testing" "github.com/stretchr/testify/assert" ) func TestChainChunk(t *testing.T) { testCases := []struct { In interface{} Size int }{ {In: []int{0, 1, 2, 3, 4}, Size: 2}, {In: []int{}, Size: 2}, {In: []int{1}, Size: 2}, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Chunk(tc.In, tc.Size) actual := Chain(tc.In).Chunk(tc.Size).Value() is.Equal(expected, actual) }) } } func TestChainCompact(t *testing.T) { var emptyFunc func() bool emptyFuncPtr := &emptyFunc nonEmptyFunc := func() bool { return true } nonEmptyFuncPtr := &nonEmptyFunc nonEmptyMap := map[int]int{1: 2} nonEmptyMapPtr := &nonEmptyMap var emptyMap map[int]int emptyMapPtr := &emptyMap var emptyChan chan bool nonEmptyChan := make(chan bool, 1) nonEmptyChan <- true emptyChanPtr := &emptyChan nonEmptyChanPtr := &nonEmptyChan var emptyString string emptyStringPtr := &emptyString nonEmptyString := "42" nonEmptyStringPtr := &nonEmptyString testCases := []struct { In interface{} }{ {In: []interface{}{42, nil, (*int)(nil)}}, {In: []interface{}{42, emptyFuncPtr, emptyFunc, nonEmptyFuncPtr}}, {In: []interface{}{42, [2]int{}, map[int]int{}, []string{}, nonEmptyMapPtr, emptyMap, emptyMapPtr, nonEmptyMap, nonEmptyChan, emptyChan, emptyChanPtr, nonEmptyChanPtr}}, {In: []interface{}{true, 0, float64(0), "", "42", emptyStringPtr, nonEmptyStringPtr, false}}, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Compact(tc.In) actual := Chain(tc.In).Compact().Value() is.Equal(expected, actual) }) } } func TestChainDrop(t *testing.T) { testCases := []struct { In interface{} N int }{ {In: []int{0, 1, 1, 2, 3, 0, 0, 12}, N: 3}, // Bug: Issues from go-funk (n parameter can be greater than len(in)) // {In: []int{0, 1}, N: 3}, // {In: []int{}, N: 3}, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Drop(tc.In, tc.N) actual := Chain(tc.In).Drop(tc.N).Value() is.Equal(expected, actual) }) } } func TestChainFilter(t *testing.T) { testCases := []struct { In interface{} Predicate interface{} }{ { In: []int{1, 2, 3, 4}, Predicate: func(x int) bool { return x%2 == 0 }, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Filter(tc.In, tc.Predicate) actual := Chain(tc.In).Filter(tc.Predicate).Value() is.Equal(expected, actual) }) } } func TestChainFilter_SideEffect(t *testing.T) { is := assert.New(t) type foo struct { bar string } in := []*foo{&foo{"foo"}, &foo{"bar"}} chain := Chain(in) is.Equal([]*foo{&foo{"foo"}, &foo{"bar"}}, chain.Value()) filtered := chain.Filter(func(x *foo) bool { x.bar = "__" + x.bar + "__" return x.bar == "foo" }) is.Equal([]*foo{}, filtered.Value()) // Side effect: in and chain.Value modified is.NotEqual([]*foo{&foo{"foo"}, &foo{"bar"}}, chain.Value()) is.NotEqual([]*foo{&foo{"foo"}, &foo{"bar"}}, in) } func TestChainFlatten(t *testing.T) { testCases := []struct { In interface{} }{ { In: [][]int{{1, 2}, {3, 4}}, }, { In: [][][]int{{{1, 2}, {3, 4}}, {{5, 6}}}, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Flatten(tc.In) actual := Chain(tc.In).Flatten().Value() is.Equal(expected, actual) }) } } func TestChainFlattenDeep(t *testing.T) { testCases := []struct { In interface{} }{ { In: [][]int{{1, 2}, {3, 4}}, }, { In: [][][]int{{{1, 2}, {3, 4}}, {{5, 6}}}, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := FlattenDeep(tc.In) actual := Chain(tc.In).FlattenDeep().Value() is.Equal(expected, actual) }) } } func TestChainInitial(t *testing.T) { testCases := []struct { In interface{} }{ { In: []int{}, }, { In: []int{0}, }, { In: []int{0, 1, 2, 3}, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Initial(tc.In) actual := Chain(tc.In).Initial().Value() is.Equal(expected, actual) }) } } func TestChainIntersect(t *testing.T) { testCases := []struct { In interface{} Sec interface{} }{ { In: []int{1, 2, 3, 4}, Sec: []int{2, 4, 6}, }, { In: []string{"foo", "bar", "hello", "bar"}, Sec: []string{"foo", "bar"}, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Intersect(tc.In, tc.Sec) actual := Chain(tc.In).Intersect(tc.Sec).Value() is.Equal(expected, actual) }) } } func TestChainMap(t *testing.T) { testCases := []struct { In interface{} MapFnc interface{} }{ { In: []int{1, 2, 3, 4}, MapFnc: func(x int) string { return "Hello" }, }, { In: []int{1, 2, 3, 4}, MapFnc: func(x int) (int, int) { return x, x }, }, { In: map[int]string{1: "Florent", 2: "Gilles"}, MapFnc: func(k int, v string) int { return k }, }, { In: map[int]string{1: "Florent", 2: "Gilles"}, MapFnc: func(k int, v string) (string, string) { return fmt.Sprintf("%d", k), v }, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Map(tc.In, tc.MapFnc) actual := Chain(tc.In).Map(tc.MapFnc).Value() if reflect.TypeOf(expected).Kind() == reflect.Map { is.Equal(expected, actual) } else { is.ElementsMatch(expected, actual) } }) } } func TestChainFlatMap(t *testing.T) { testCases := []struct { In interface{} FlatMapFnc interface{} }{ { In: [][]int{{1}, {2}, {3}, {4}}, FlatMapFnc: func(x []int) []int { return x }, }, { In: map[string][]int{"Florent": {1}, "Gilles": {2}}, FlatMapFnc: func(k string, v []int) []int { return v }, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := FlatMap(tc.In, tc.FlatMapFnc) actual := Chain(tc.In).FlatMap(tc.FlatMapFnc).Value() is.ElementsMatch(expected, actual) }) } } func TestChainMap_SideEffect(t *testing.T) { is := assert.New(t) type foo struct { bar string } in := []*foo{&foo{"foo"}, &foo{"bar"}} chain := Chain(in) is.Equal([]*foo{&foo{"foo"}, &foo{"bar"}}, chain.Value()) mapped := chain.Map(func(x *foo) (string, bool) { x.bar = "__" + x.bar + "__" return x.bar, x.bar == "foo" }) is.Equal(map[string]bool{"__foo__": false, "__bar__": false}, mapped.Value()) // Side effect: in and chain.Value modified is.NotEqual([]*foo{&foo{"foo"}, &foo{"bar"}}, chain.Value()) is.NotEqual([]*foo{&foo{"foo"}, &foo{"bar"}}, in) } func TestChainReverse(t *testing.T) { testCases := []struct { In interface{} }{ { In: []int{0, 1, 2, 3, 4}, }, { In: "abcdefg", }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Reverse(tc.In) actual := Chain(tc.In).Reverse().Value() is.Equal(expected, actual) }) } } func TestChainShuffle(t *testing.T) { testCases := []struct { In interface{} }{ { In: []int{0, 1, 2, 3, 4}, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Shuffle(tc.In) actual := Chain(tc.In).Shuffle().Value() is.NotEqual(expected, actual) is.ElementsMatch(expected, actual) }) } } func TestChainTail(t *testing.T) { testCases := []struct { In interface{} }{ { In: []int{}, }, { In: []int{0}, }, { In: []int{0, 1, 2, 3}, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Tail(tc.In) actual := Chain(tc.In).Tail().Value() is.Equal(expected, actual) }) } } func TestChainUniq(t *testing.T) { testCases := []struct { In interface{} }{ { In: []int{0, 1, 1, 2, 3, 0, 0, 12}, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Uniq(tc.In) actual := Chain(tc.In).Uniq().Value() is.Equal(expected, actual) }) } } func TestChainAll(t *testing.T) { testCases := []struct { In []interface{} }{ {In: []interface{}{"foo", "bar"}}, {In: []interface{}{"foo", ""}}, {In: []interface{}{"", ""}}, {In: []interface{}{}}, {In: []interface{}{true, "foo", 6}}, {In: []interface{}{true, "", 6}}, {In: []interface{}{true, "foo", 0}}, {In: []interface{}{false, "foo", 6}}, {In: []interface{}{false, "", 0}}, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := All(tc.In...) actual := Chain(tc.In).All() is.Equal(expected, actual) }) } } func TestChainAny(t *testing.T) { testCases := []struct { In []interface{} }{ {In: []interface{}{"foo", "bar"}}, {In: []interface{}{"foo", ""}}, {In: []interface{}{"", ""}}, {In: []interface{}{}}, {In: []interface{}{true, "foo", 6}}, {In: []interface{}{true, "", 6}}, {In: []interface{}{true, "foo", 0}}, {In: []interface{}{false, "foo", 6}}, {In: []interface{}{false, "", 0}}, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Any(tc.In...) actual := Chain(tc.In).Any() is.Equal(expected, actual) }) } } func TestChainContains(t *testing.T) { testCases := []struct { In interface{} Contains interface{} }{ { In: []string{"foo", "bar"}, Contains: "bar", }, { In: []string{"foo", "bar"}, Contains: func (value string) bool { return value == "bar" }, }, { In: results, Contains: f, }, { In: results, Contains: nil, }, { In: results, Contains: b, }, { In: "florent", Contains: "rent", }, { In: "florent", Contains: "gilles", }, { In: map[int]*Foo{1: f, 3: c}, Contains: 1, }, { In: map[int]*Foo{1: f, 3: c}, Contains: 2, }, { In: map[int]*Foo{1: f, 3: c}, Contains: func (key int, foo *Foo) bool { return key == 3 && foo.FirstName == "Harald" }, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Contains(tc.In, tc.Contains) actual := Chain(tc.In).Contains(tc.Contains) is.Equal(expected, actual) }) } } func TestChainEvery(t *testing.T) { testCases := []struct { In interface{} Contains []interface{} }{ { In: []string{"foo", "bar", "baz"}, Contains: []interface{}{"bar", "foo"}, }, { In: results, Contains: []interface{}{f, c}, }, { In: results, Contains: []interface{}{nil}, }, { In: results, Contains: []interface{}{f, b}, }, { In: "florent", Contains: []interface{}{"rent", "flo"}, }, { In: "florent", Contains: []interface{}{"rent", "gilles"}, }, { In: map[int]*Foo{1: f, 3: c}, Contains: []interface{}{1, 3}, }, { In: map[int]*Foo{1: f, 3: c}, Contains: []interface{}{2, 3}, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Every(tc.In, tc.Contains...) actual := Chain(tc.In).Every(tc.Contains...) is.Equal(expected, actual) }) } } func TestChainFind(t *testing.T) { testCases := []struct { In interface{} Predicate interface{} }{ { In: []int{1, 2, 3, 4}, Predicate: func(x int) bool { return x%2 == 0 }, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Find(tc.In, tc.Predicate) actual := Chain(tc.In).Find(tc.Predicate) is.Equal(expected, actual) }) } } func TestChainFind_SideEffect(t *testing.T) { is := assert.New(t) type foo struct { bar string } in := []*foo{&foo{"foo"}, &foo{"bar"}} chain := Chain(in) is.Equal([]*foo{&foo{"foo"}, &foo{"bar"}}, chain.Value()) result := chain.Find(func(x *foo) bool { x.bar = "__" + x.bar + "__" return x.bar == "foo" }) is.Nil(result) // Side effect: in and chain.Value modified is.NotEqual([]*foo{&foo{"foo"}, &foo{"bar"}}, chain.Value()) is.NotEqual([]*foo{&foo{"foo"}, &foo{"bar"}}, in) } func TestChainForEach(t *testing.T) { var expectedAcc, actualAcc []interface{} testCases := []struct { In interface{} FunkIterator interface{} ChainIterator interface{} }{ { In: []int{1, 2, 3, 4}, FunkIterator: func(x int) { if x%2 == 0 { expectedAcc = append(expectedAcc, x) } }, ChainIterator: func(x int) { if x%2 == 0 { actualAcc = append(actualAcc, x) } }, }, { In: map[int]string{1: "Florent", 2: "Gilles"}, FunkIterator: func(k int, v string) { expectedAcc = append(expectedAcc, fmt.Sprintf("%d:%s", k, v)) }, ChainIterator: func(k int, v string) { actualAcc = append(actualAcc, fmt.Sprintf("%d:%s", k, v)) }, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expectedAcc = []interface{}{} actualAcc = []interface{}{} ForEach(tc.In, tc.FunkIterator) Chain(tc.In).ForEach(tc.ChainIterator) is.ElementsMatch(expectedAcc, actualAcc) }) } } func TestChainForEach_SideEffect(t *testing.T) { is := assert.New(t) type foo struct { bar string } var out []*foo in := []*foo{&foo{"foo"}, &foo{"bar"}} chain := Chain(in) is.Equal([]*foo{&foo{"foo"}, &foo{"bar"}}, chain.Value()) chain.ForEach(func(x *foo) { x.bar = "__" + x.bar + "__" out = append(out, x) }) is.Equal([]*foo{&foo{"__foo__"}, &foo{"__bar__"}}, out) // Side effect: in and chain.Value modified is.NotEqual([]*foo{&foo{"foo"}, &foo{"bar"}}, chain.Value()) is.NotEqual([]*foo{&foo{"foo"}, &foo{"bar"}}, in) } func TestChainForEachRight(t *testing.T) { var expectedAcc, actualAcc []interface{} testCases := []struct { In interface{} FunkIterator interface{} ChainIterator interface{} }{ { In: []int{1, 2, 3, 4}, FunkIterator: func(x int) { if x%2 == 0 { expectedAcc = append(expectedAcc, x) } }, ChainIterator: func(x int) { if x%2 == 0 { actualAcc = append(actualAcc, x) } }, }, { In: map[int]string{1: "Florent", 2: "Gilles"}, FunkIterator: func(k int, v string) { expectedAcc = append(expectedAcc, fmt.Sprintf("%d:%s", k, v)) }, ChainIterator: func(k int, v string) { actualAcc = append(actualAcc, fmt.Sprintf("%d:%s", k, v)) }, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expectedAcc = []interface{}{} actualAcc = []interface{}{} ForEachRight(tc.In, tc.FunkIterator) Chain(tc.In).ForEachRight(tc.ChainIterator) is.ElementsMatch(expectedAcc, actualAcc) }) } } func TestChainForEachRight_SideEffect(t *testing.T) { is := assert.New(t) type foo struct { bar string } var out []*foo in := []*foo{&foo{"foo"}, &foo{"bar"}} chain := Chain(in) is.Equal([]*foo{&foo{"foo"}, &foo{"bar"}}, chain.Value()) chain.ForEachRight(func(x *foo) { x.bar = "__" + x.bar + "__" out = append(out, x) }) is.Equal([]*foo{&foo{"__bar__"}, &foo{"__foo__"}}, out) // Side effect: in and chain.Value modified is.NotEqual([]*foo{&foo{"foo"}, &foo{"bar"}}, chain.Value()) is.NotEqual([]*foo{&foo{"foo"}, &foo{"bar"}}, in) } func TestChainHead(t *testing.T) { testCases := []struct { In interface{} }{ { In: []int{1, 2, 3, 4}, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Head(tc.In) actual := Chain(tc.In).Head() is.Equal(expected, actual) }) } } func TestChainKeys(t *testing.T) { testCases := []struct { In interface{} }{ {In: map[string]int{"one": 1, "two": 2}}, {In: &map[string]int{"one": 1, "two": 2}}, {In: map[int]complex128{5: 1 + 8i, 3: 2}}, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Keys(tc.In) actual := Chain(tc.In).Keys() is.ElementsMatch(expected, actual) }) } } func TestChainIndexOf(t *testing.T) { testCases := []struct { In interface{} Item interface{} }{ { In: []string{"foo", "bar"}, Item: "bar", }, { In: []string{"foo", "bar"}, Item: func (value string) bool { return value == "bar" }, }, { In: results, Item: f, }, { In: results, Item: b, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := IndexOf(tc.In, tc.Item) actual := Chain(tc.In).IndexOf(tc.Item) is.Equal(expected, actual) }) } } func TestChainIsEmpty(t *testing.T) { testCases := []struct { In interface{} }{ {In: ""}, {In: [0]interface{}{}}, {In: []interface{}(nil)}, {In: map[interface{}]interface{}(nil)}, {In: "s"}, {In: [1]interface{}{1}}, {In: []interface{}{}}, {In: map[interface{}]interface{}{}}, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := IsEmpty(tc.In) actual := Chain(tc.In).IsEmpty() is.Equal(expected, actual) }) } } func TestChainLast(t *testing.T) { testCases := []struct { In interface{} }{ { In: []int{1, 2, 3, 4}, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Last(tc.In) actual := Chain(tc.In).Last() is.Equal(expected, actual) }) } } func TestChainLastIndexOf(t *testing.T) { testCases := []struct { In interface{} Item interface{} }{ { In: []string{"foo", "bar", "bar"}, Item: "bar", }, { In: []string{"foo", "bar", "bar"}, Item: func (value string) bool { return value == "bar" }, }, { In: []int{1, 2, 2, 3}, Item: 2, }, { In: []int{1, 2, 2, 3}, Item: 4, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := LastIndexOf(tc.In, tc.Item) actual := Chain(tc.In).LastIndexOf(tc.Item) is.Equal(expected, actual) }) } } func TestChainNotEmpty(t *testing.T) { testCases := []struct { In interface{} }{ {In: ""}, {In: [0]interface{}{}}, {In: []interface{}(nil)}, {In: map[interface{}]interface{}(nil)}, {In: "s"}, {In: [1]interface{}{1}}, {In: []interface{}{}}, {In: map[interface{}]interface{}{}}, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := NotEmpty(tc.In) actual := Chain(tc.In).NotEmpty() is.Equal(expected, actual) }) } } func TestChainProduct(t *testing.T) { testCases := []struct { In interface{} }{ {In: []int{0, 1, 2, 3}}, {In: &[]int{0, 1, 2, 3}}, {In: []interface{}{1, 2, 3, 0.5}}, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Product(tc.In) actual := Chain(tc.In).Product() is.Equal(expected, actual) }) } } func TestChainReduce(t *testing.T) { testCases := []struct { In interface{} ReduceFunc interface{} Acc interface{} }{ { In: []int{1, 2, 3, 4}, ReduceFunc: func(acc, elem int) int { return acc + elem }, Acc: 0, }, { In: &[]int16{1, 2, 3, 4}, ReduceFunc: '+', Acc: 5, }, { In: []float64{1.1, 2.2, 3.3}, ReduceFunc: '+', Acc: 0, }, { In: &[]int{1, 2, 3, 5}, ReduceFunc: func(acc int8, elem int16) int32 { return int32(acc) * int32(elem) }, Acc: 1, }, { In: []interface{}{1, 2, 3.3, 4}, ReduceFunc: '*', Acc: 1, }, { In: []string{"1", "2", "3", "4"}, ReduceFunc: func(acc string, elem string) string { return acc + elem }, Acc: "", }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Reduce(tc.In, tc.ReduceFunc, tc.Acc) actual := Chain(tc.In).Reduce(tc.ReduceFunc, tc.Acc) is.Equal(expected, actual) }) } } func TestChainSum(t *testing.T) { testCases := []struct { In interface{} }{ {In: []int{0, 1, 2, 3}}, {In: &[]int{0, 1, 2, 3}}, {In: []interface{}{1, 2, 3, 0.5}}, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Sum(tc.In) actual := Chain(tc.In).Sum() is.Equal(expected, actual) }) } } func TestChainType(t *testing.T) { type key string var x key testCases := []struct { In interface{} }{ {In: []string{}}, {In: []int{}}, {In: []bool{}}, {In: []interface{}{}}, {In: &[]interface{}{}}, {In: map[int]string{}}, {In: map[complex128]int{}}, {In: map[string]string{}}, {In: map[int]interface{}{}}, {In: map[key]interface{}{}}, {In: &map[key]interface{}{}}, {In: ""}, {In: &x}, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) actual := Chain(tc.In).Type() is.Equal(reflect.TypeOf(tc.In), actual) }) } } func TestChainValue(t *testing.T) { testCases := []struct { In interface{} }{ {In: []int{0, 1, 2, 3}}, {In: []string{"foo", "bar"}}, {In: &[]string{"foo", "bar"}}, {In: map[int]string{1: "foo", 2: "bar"}}, {In: map[string]string{"foo": "foo", "bar": "bar"}}, {In: &map[string]string{"foo": "foo", "bar": "bar"}}, {In: "foo"}, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) actual := Chain(tc.In).Value() is.Equal(tc.In, actual) }) } } func TestChainValues(t *testing.T) { testCases := []struct { In interface{} }{ {In: map[string]int{"one": 1, "two": 2}}, {In: &map[string]int{"one": 1, "two": 2}}, {In: map[int]complex128{5: 1 + 8i, 3: 2}}, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Values(tc.In) actual := Chain(tc.In).Values() is.ElementsMatch(expected, actual) }) } } func TestComplexChaining(t *testing.T) { is := assert.New(t) in := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} chain := Chain(in) // Without builder fa := Filter(in, func(x int) bool { return x%2 == 0 }) fb := Map(fa, func(x int) int { return x * 2 }) fc := Reverse(fa) // With simple chaining ca := chain.Filter(func(x int) bool { return x%2 == 0 }) cb := ca.Map(func(x int) int { return x * 2 }) cc := ca.Reverse() is.Equal(fa, ca.Value()) is.Equal(fb, cb.Value()) is.Equal(fc, cc.Value()) is.Equal(Contains(fb, 2), cb.Contains(2)) is.Equal(Contains(fb, 4), cb.Contains(4)) is.Equal(Sum(fb), cb.Sum()) is.Equal(Head(fb), cb.Head()) is.Equal(Head(fc), cc.Head()) } go-funk-0.9.3/compact.go000066400000000000000000000021431435240704200150470ustar00rootroot00000000000000package funk import ( "reflect" ) // Compact creates a slice with all empty/zero values removed. func Compact(value interface{}) interface{} { arr := redirectValue(reflect.ValueOf(value)) if arr.Kind() != reflect.Slice && arr.Kind() != reflect.Array { panic("First parameter must be array or slice") } sliceElemType := sliceElem(arr.Type()) resultSliceType := reflect.SliceOf(sliceElemType) result := reflect.MakeSlice(resultSliceType, 0, 0) for i := 0; i < arr.Len(); i++ { elemVal := arr.Index(i) if elemVal.Kind() == reflect.Interface { elemVal = elemVal.Elem() } redirectedElemVal := redirectValue(elemVal) switch redirectedElemVal.Kind() { case reflect.Invalid: continue case reflect.Func: if redirectedElemVal.IsNil() { continue } case reflect.Map, reflect.Slice, reflect.Chan: if redirectedElemVal.Len() == 0 { continue } default: defaultValue := reflect.Zero(redirectedElemVal.Type()).Interface() if redirectedElemVal.Interface() == defaultValue { continue } } result = reflect.Append(result, elemVal) } return result.Interface() } go-funk-0.9.3/compact_test.go000066400000000000000000000033011435240704200161030ustar00rootroot00000000000000package funk import ( "fmt" "testing" "github.com/stretchr/testify/assert" ) func TestCompact(t *testing.T) { var emptyFunc func() bool emptyFuncPtr := &emptyFunc nonEmptyFunc := func() bool { return true } nonEmptyFuncPtr := &nonEmptyFunc nonEmptyMap := map[int]int{1: 2} nonEmptyMapPtr := &nonEmptyMap var emptyMap map[int]int emptyMapPtr := &emptyMap var emptyChan chan bool nonEmptyChan := make(chan bool, 1) nonEmptyChan <- true emptyChanPtr := &emptyChan nonEmptyChanPtr := &nonEmptyChan var emptyString string emptyStringPtr := &emptyString nonEmptyString := "42" nonEmptyStringPtr := &nonEmptyString testCases := []struct { Arr interface{} Result interface{} }{ // Check with nils { []interface{}{42, nil, (*int)(nil)}, []interface{}{42}, }, // Check with functions { []interface{}{42, emptyFuncPtr, emptyFunc, nonEmptyFuncPtr}, []interface{}{42, nonEmptyFuncPtr}, }, // Check with slices, maps, arrays and channels { []interface{}{ 42, [2]int{}, map[int]int{}, []string{}, nonEmptyMapPtr, emptyMap, emptyMapPtr, nonEmptyMap, nonEmptyChan, emptyChan, emptyChanPtr, nonEmptyChanPtr, }, []interface{}{42, nonEmptyMapPtr, nonEmptyMap, nonEmptyChan, nonEmptyChanPtr}, }, // Check with strings, numbers and booleans { []interface{}{true, 0, float64(0), "", "42", emptyStringPtr, nonEmptyStringPtr, false}, []interface{}{true, "42", nonEmptyStringPtr}, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) result := Compact(tc.Arr) if !is.Equal(result, tc.Result) { t.Errorf("%#v doesn't equal to %#v", result, tc.Result) } }) } } go-funk-0.9.3/example_presence_test.go000066400000000000000000000003211435240704200177730ustar00rootroot00000000000000package funk import "fmt" func ExampleSome() { a := []string{"foo", "bar", "baz"} fmt.Println(Some(a, "foo", "qux")) b := "Mark Shaun" fmt.Println(Some(b, "Marc", "Sean")) // Output: true // false } go-funk-0.9.3/fill.go000066400000000000000000000014601435240704200143500ustar00rootroot00000000000000package funk import ( "errors" "fmt" "reflect" ) // Fill fills elements of array with value func Fill(in interface{}, fillValue interface{}) (interface{}, error) { inValue := reflect.ValueOf(in) inKind := inValue.Type().Kind() if inKind != reflect.Slice && inKind != reflect.Array { return nil, errors.New("Can only fill slices and arrays") } inType := reflect.TypeOf(in).Elem() value := reflect.ValueOf(fillValue) if inType != value.Type() { return nil, fmt.Errorf( "Cannot fill '%s' with '%s'", reflect.TypeOf(in), value.Type(), ) } length := inValue.Len() newSlice := reflect.SliceOf(reflect.TypeOf(fillValue)) in = reflect.MakeSlice(newSlice, length, length).Interface() inValue = reflect.ValueOf(in) for i := 0; i < length; i++ { inValue.Index(i).Set(value) } return in, nil } go-funk-0.9.3/fill_test.go000066400000000000000000000020321435240704200154030ustar00rootroot00000000000000package funk import ( "github.com/stretchr/testify/assert" "testing" ) func TestFillMismatchedTypes(t *testing.T) { _, err := Fill([]string{"a", "b"}, 1) assert.EqualError(t, err, "Cannot fill '[]string' with 'int'") } func TestFillUnfillableTypes(t *testing.T) { var stringVariable string var uint32Variable uint32 var boolVariable bool types := [](interface{}){ stringVariable, uint32Variable, boolVariable, } for _, unfillable := range types { _, err := Fill(unfillable, 1) assert.EqualError(t, err, "Can only fill slices and arrays") } } func TestFillSlice(t *testing.T) { input := []int{1, 2, 3} result, err := Fill(input, 1) assert.NoError(t, err) assert.Equal(t, []int{1, 1, 1}, result) // Assert that input does not change assert.Equal(t, []int{1, 2, 3}, input) } func TestFillArray(t *testing.T) { input := [...]int{1, 2, 3} result, err := Fill(input, 2) assert.NoError(t, err) assert.Equal(t, []int{2, 2, 2}, result) // Assert that input does not change assert.Equal(t, [...]int{1, 2, 3}, input) } go-funk-0.9.3/funk_test.go000066400000000000000000000041061435240704200154240ustar00rootroot00000000000000package funk import "database/sql" type Model interface { TableName() string } // Bar is type Bar struct { Name string `tag_name:"BarName"` Bar *Bar Bars []*Bar } func (b Bar) TableName() string { return "bar" } // Foo is type Foo struct { ID int FirstName string `tag_name:"tag 1"` LastName string `tag_name:"tag 2"` Age int `tag_name:"tag 3"` Bar *Bar `tag_name:"tag 4"` Bars []*Bar EmptyValue sql.NullInt64 BarInterface interface{} BarPointer interface{} GeneralInterface interface{} ZeroBoolValue bool ZeroIntValue int ZeroIntPtrValue *int } func (f Foo) TableName() string { return "foo" } var bar = &Bar{ Name: "Test", Bars: []*Bar{ { Name: "Level1-1", Bar: &Bar{ Name: "Level2-1", }, }, { Name: "Level1-2", Bar: &Bar{ Name: "Level2-2", }, }, }, } var foo = &Foo{ ID: 1, FirstName: "Dark", LastName: "Vador", Age: 30, Bar: bar, EmptyValue: sql.NullInt64{ Valid: true, Int64: 10, }, Bars: []*Bar{ bar, bar, }, BarInterface: bar, BarPointer: &bar, ZeroBoolValue: false, ZeroIntValue: 0, ZeroIntPtrValue: nil, } var foo2 = &Foo{ ID: 1, FirstName: "Dark", LastName: "Vador", Age: 30, } var m1 = map[string]interface{}{ "id": 1, "firstname": "dark", "lastname": "vador", "age": 30, "bar": map[string]interface{}{ "name": "test", "bars": []map[string]interface{}{ { "name": "level1-1", "bar": map[string]interface{}{ "name": "level2-1", }, }, { "name": "level1-2", "bar": map[string]interface{}{ "name": "level2-2", }, }, }, }, } var m2 = map[string]interface{}{ "id": 1, "firstname": "dark", "lastname": "vador", "age": 30, } type FooUnexported struct { unexported bool } var fooUnexported = &FooUnexported{ unexported: true, } type EmbeddedStruct struct { EmbeddedField *string } type RootStructPointer struct { *EmbeddedStruct RootField *string } type RootStructNotPointer struct { EmbeddedStruct RootField *string } go-funk-0.9.3/go.mod000066400000000000000000000001251435240704200141760ustar00rootroot00000000000000module github.com/thoas/go-funk go 1.13 require github.com/stretchr/testify v1.4.0 go-funk-0.9.3/go.sum000066400000000000000000000017101435240704200142240ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= go-funk-0.9.3/helpers.go000066400000000000000000000145551435240704200150750ustar00rootroot00000000000000package funk import ( "bytes" "math/rand" "reflect" ) var numericZeros = []interface{}{ int(0), int8(0), int16(0), int32(0), int64(0), uint(0), uint8(0), uint16(0), uint32(0), uint64(0), float32(0), float64(0), } // ToFloat64 converts any numeric value to float64. func ToFloat64(x interface{}) (float64, bool) { var xf float64 xok := true switch xn := x.(type) { case uint8: xf = float64(xn) case uint16: xf = float64(xn) case uint32: xf = float64(xn) case uint64: xf = float64(xn) case int: xf = float64(xn) case int8: xf = float64(xn) case int16: xf = float64(xn) case int32: xf = float64(xn) case int64: xf = float64(xn) case float32: xf = float64(xn) case float64: xf = float64(xn) default: xok = false } return xf, xok } // PtrOf makes a copy of the given interface and returns a pointer. func PtrOf(itf interface{}) interface{} { t := reflect.TypeOf(itf) cp := reflect.New(t) cp.Elem().Set(reflect.ValueOf(itf)) // Avoid double pointers if itf is a pointer if t.Kind() == reflect.Ptr { return cp.Elem().Interface() } return cp.Interface() } // IsFunction returns if the argument is a function. func IsFunction(in interface{}, num ...int) bool { funcType := reflect.TypeOf(in) result := funcType != nil && funcType.Kind() == reflect.Func if len(num) >= 1 { result = result && funcType.NumIn() == num[0] } if len(num) == 2 { result = result && funcType.NumOut() == num[1] } return result } // IsPredicate returns if the argument is a predicate function. func IsPredicate(in interface{}, inTypes ...reflect.Type) bool { if len(inTypes) == 0 { inTypes = append(inTypes, nil) } funcType := reflect.TypeOf(in) result := funcType != nil && funcType.Kind() == reflect.Func result = result && funcType.NumOut() == 1 && funcType.Out(0).Kind() == reflect.Bool result = result && funcType.NumIn() == len(inTypes) for i := 0; result && i < len(inTypes); i++ { inType := inTypes[i] result = inType == nil || inType.ConvertibleTo(funcType.In(i)) } return result } // IsEqual returns if the two objects are equal func IsEqual(expected interface{}, actual interface{}) bool { if expected == nil || actual == nil { return expected == actual } if exp, ok := expected.([]byte); ok { act, ok := actual.([]byte) if !ok { return false } if exp == nil || act == nil { return true } return bytes.Equal(exp, act) } return reflect.DeepEqual(expected, actual) } // IsType returns if the two objects are in the same type func IsType(expected interface{}, actual interface{}) bool { return IsEqual(reflect.TypeOf(expected), reflect.TypeOf(actual)) } // Equal returns if the two objects are equal func Equal(expected interface{}, actual interface{}) bool { return IsEqual(expected, actual) } // NotEqual returns if the two objects are not equal func NotEqual(expected interface{}, actual interface{}) bool { return !IsEqual(expected, actual) } // IsIteratee returns if the argument is an iteratee. func IsIteratee(in interface{}) bool { if in == nil { return false } arrType := reflect.TypeOf(in) kind := arrType.Kind() return kind == reflect.Array || kind == reflect.Slice || kind == reflect.Map } // IsCollection returns if the argument is a collection. func IsCollection(in interface{}) bool { arrType := reflect.TypeOf(in) kind := arrType.Kind() return kind == reflect.Array || kind == reflect.Slice } // SliceOf returns a slice which contains the element. func SliceOf(in interface{}) interface{} { value := reflect.ValueOf(in) sliceType := reflect.SliceOf(reflect.TypeOf(in)) slice := reflect.New(sliceType) sliceValue := reflect.MakeSlice(sliceType, 0, 0) sliceValue = reflect.Append(sliceValue, value) slice.Elem().Set(sliceValue) return slice.Elem().Interface() } // Any returns true if any element of the iterable is not empty. If the iterable is empty, return False. func Any(objs ...interface{}) bool { if len(objs) == 0 { return false } for _, obj := range objs { if !IsEmpty(obj) { return true } } return false } // All returns true if all elements of the iterable are not empty (or if the iterable is empty) func All(objs ...interface{}) bool { if len(objs) == 0 { return true } for _, obj := range objs { if IsEmpty(obj) { return false } } return true } // IsEmpty returns if the object is considered as empty or not. func IsEmpty(obj interface{}) bool { if obj == nil || obj == "" || obj == false { return true } for _, v := range numericZeros { if obj == v { return true } } objValue := reflect.ValueOf(obj) switch objValue.Kind() { case reflect.Map: fallthrough case reflect.Slice, reflect.Chan: return objValue.Len() == 0 case reflect.Struct: return reflect.DeepEqual(obj, ZeroOf(obj)) case reflect.Ptr: if objValue.IsNil() { return true } obj = redirectValue(objValue).Interface() return reflect.DeepEqual(obj, ZeroOf(obj)) } return false } // IsZero returns if the object is considered as zero value func IsZero(obj interface{}) bool { if obj == nil || obj == "" || obj == false { return true } for _, v := range numericZeros { if obj == v { return true } } return reflect.DeepEqual(obj, ZeroOf(obj)) } // NotEmpty returns if the object is considered as non-empty or not. func NotEmpty(obj interface{}) bool { return !IsEmpty(obj) } // ZeroOf returns a zero value of an element. func ZeroOf(in interface{}) interface{} { if in == nil { return nil } return reflect.Zero(reflect.TypeOf(in)).Interface() } // RandomInt generates a random int, based on a min and max values func RandomInt(min, max int) int { return min + rand.Intn(max-min) } // Shard will shard a string name func Shard(str string, width int, depth int, restOnly bool) []string { var results []string for i := 0; i < depth; i++ { results = append(results, str[(width*i):(width*(i+1))]) } if restOnly { results = append(results, str[(width*depth):]) } else { results = append(results, str) } return results } var defaultLetters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") // RandomString returns a random string with a fixed length func RandomString(n int, allowedChars ...[]rune) string { var letters []rune if len(allowedChars) == 0 { letters = defaultLetters } else { letters = allowedChars[0] } b := make([]rune, n) for i := range b { b[i] = letters[rand.Intn(len(letters))] } return string(b) } go-funk-0.9.3/helpers_test.go000066400000000000000000000166421435240704200161330ustar00rootroot00000000000000package funk import ( "errors" "os" "reflect" "testing" "time" "github.com/stretchr/testify/assert" ) var ( i interface{} zeros = []interface{}{ false, byte(0), complex64(0), complex128(0), float32(0), float64(0), int(0), int8(0), int16(0), int32(0), int64(0), rune(0), uint(0), uint8(0), uint16(0), uint32(0), uint64(0), uintptr(0), "", [0]interface{}{}, []interface{}(nil), struct{ x int }{}, (*interface{})(nil), (func())(nil), nil, interface{}(nil), map[interface{}]interface{}(nil), (chan interface{})(nil), (<-chan interface{})(nil), (chan<- interface{})(nil), } nonZeros = []interface{}{ true, byte(1), complex64(1), complex128(1), float32(1), float64(1), int(1), int8(1), int16(1), int32(1), int64(1), rune(1), uint(1), uint8(1), uint16(1), uint32(1), uint64(1), uintptr(1), "s", [1]interface{}{1}, []interface{}{}, struct{ x int }{1}, (*interface{})(&i), (func())(func() {}), interface{}(1), map[interface{}]interface{}{}, (chan interface{})(make(chan interface{})), (<-chan interface{})(make(chan interface{})), (chan<- interface{})(make(chan interface{})), } ) func TestPtrOf(t *testing.T) { is := assert.New(t) type embedType struct { value int } type anyType struct { value int embed embedType embedPtr *embedType } any := anyType{value: 1} anyPtr := &anyType{value: 1} results := []interface{}{ PtrOf(any), PtrOf(anyPtr), } for _, r := range results { is.Equal(1, r.(*anyType).value) is.Equal(reflect.ValueOf(r).Kind(), reflect.Ptr) is.Equal(reflect.ValueOf(r).Type().Elem(), reflect.TypeOf(anyType{})) } anyWithEmbed := anyType{value: 1, embed: embedType{value: 2}} anyWithEmbedPtr := anyType{value: 1, embedPtr: &embedType{value: 2}} results = []interface{}{ PtrOf(anyWithEmbed.embed), PtrOf(anyWithEmbedPtr.embedPtr), } for _, r := range results { is.Equal(2, r.(*embedType).value) is.Equal(reflect.ValueOf(r).Kind(), reflect.Ptr) is.Equal(reflect.ValueOf(r).Type().Elem(), reflect.TypeOf(embedType{})) } } func TestSliceOf(t *testing.T) { is := assert.New(t) f := &Foo{ ID: 1, FirstName: "Dark", LastName: "Vador", Age: 30, Bar: &Bar{ Name: "Test", }, } result := SliceOf(f) resultType := reflect.TypeOf(result) is.True(resultType.Kind() == reflect.Slice) is.True(resultType.Elem().Kind() == reflect.Ptr) elemType := resultType.Elem().Elem() is.True(elemType.Kind() == reflect.Struct) value := reflect.ValueOf(result) is.Equal(value.Len(), 1) _, ok := value.Index(0).Interface().(*Foo) is.True(ok) } func TestRandomInt(t *testing.T) { is := assert.New(t) is.True(RandomInt(0, 10) <= 10) } func TestShard(t *testing.T) { is := assert.New(t) tokey := "e89d66bdfdd4dd26b682cc77e23a86eb" is.Equal(Shard(tokey, 1, 2, false), []string{"e", "8", "e89d66bdfdd4dd26b682cc77e23a86eb"}) is.Equal(Shard(tokey, 2, 2, false), []string{"e8", "9d", "e89d66bdfdd4dd26b682cc77e23a86eb"}) is.Equal(Shard(tokey, 2, 3, true), []string{"e8", "9d", "66", "bdfdd4dd26b682cc77e23a86eb"}) } func TestRandomString(t *testing.T) { is := assert.New(t) is.Len(RandomString(10), 10) result := RandomString(10, []rune("abcdefg")) is.Len(result, 10) for _, char := range result { is.True(char >= []rune("a")[0] && char <= []rune("g")[0]) } } func TestIsEmpty(t *testing.T) { is := assert.New(t) chWithValue := make(chan struct{}, 1) chWithValue <- struct{}{} var tiP *time.Time var tiNP time.Time var s *string var f *os.File ptrs := new(string) *ptrs = "" is.True(IsEmpty(ptrs), "Nil string pointer is empty") is.True(IsEmpty(""), "Empty string is empty") is.True(IsEmpty(nil), "Nil is empty") is.True(IsEmpty([]string{}), "Empty string array is empty") is.True(IsEmpty(0), "Zero int value is empty") is.True(IsEmpty(false), "False value is empty") is.True(IsEmpty(make(chan struct{})), "Channel without values is empty") is.True(IsEmpty(s), "Nil string pointer is empty") is.True(IsEmpty(f), "Nil os.File pointer is empty") is.True(IsEmpty(tiP), "Nil time.Time pointer is empty") is.True(IsEmpty(tiNP), "time.Time is empty") is.False(NotEmpty(ptrs), "Nil string pointer is empty") is.False(NotEmpty(""), "Empty string is empty") is.False(NotEmpty(nil), "Nil is empty") is.False(NotEmpty([]string{}), "Empty string array is empty") is.False(NotEmpty(0), "Zero int value is empty") is.False(NotEmpty(false), "False value is empty") is.False(NotEmpty(make(chan struct{})), "Channel without values is empty") is.False(NotEmpty(s), "Nil string pointer is empty") is.False(NotEmpty(f), "Nil os.File pointer is empty") is.False(NotEmpty(tiP), "Nil time.Time pointer is empty") is.False(NotEmpty(tiNP), "time.Time is empty") is.False(IsEmpty("something"), "Non Empty string is not empty") is.False(IsEmpty(errors.New("something")), "Non nil object is not empty") is.False(IsEmpty([]string{"something"}), "Non empty string array is not empty") is.False(IsEmpty(1), "Non-zero int value is not empty") is.False(IsEmpty(true), "True value is not empty") is.False(IsEmpty(chWithValue), "Channel with values is not empty") is.True(NotEmpty("something"), "Non Empty string is not empty") is.True(NotEmpty(errors.New("something")), "Non nil object is not empty") is.True(NotEmpty([]string{"something"}), "Non empty string array is not empty") is.True(NotEmpty(1), "Non-zero int value is not empty") is.True(NotEmpty(true), "True value is not empty") is.True(NotEmpty(chWithValue), "Channel with values is not empty") } func TestIsZero(t *testing.T) { is := assert.New(t) for _, test := range zeros { is.True(IsZero(test)) } for _, test := range nonZeros { is.False(IsZero(test)) } } func TestAny(t *testing.T) { is := assert.New(t) is.True(Any(true, false)) is.True(Any(true, true)) is.False(Any(false, false)) is.False(Any("", nil, false)) } func TestAll(t *testing.T) { is := assert.New(t) is.False(All(true, false)) is.True(All(true, true)) is.False(All(false, false)) is.False(All("", nil, false)) is.True(All("foo", true, 3)) } func TestIsIteratee(t *testing.T) { is := assert.New(t) is.False(IsIteratee(nil)) } func TestIsFunction(t *testing.T) { is := assert.New(t) is.False(IsFunction(nil)) is.False(IsFunction("")) is.True(IsFunction(func() {})) is.True(IsFunction(func(string, string, string) bool { return false }, 3)) is.False(IsFunction(func(string, string, string) bool { return false }, 3, 0)) is.True(IsFunction(func(string, string, string) (bool, error) { return false, nil }, 3, 2)) } func TestIsPredicate(t *testing.T) { is := assert.New(t) is.False(IsPredicate(nil)) is.False(IsPredicate("")) is.False(IsPredicate(func() {})) is.False(IsPredicate(func() bool { return false})) is.True(IsPredicate(func(int) bool { return false})) is.True(IsPredicate(func(int) bool { return false}, nil)) is.False(IsPredicate(func(int) bool { return false}, reflect.TypeOf(""))) is.True(IsPredicate(func(int) bool { return false}, reflect.TypeOf(0))) is.False(IsPredicate(func(int, string) bool { return false}, reflect.TypeOf(""))) is.False(IsPredicate(func(int, string) bool { return false}, reflect.TypeOf(""), reflect.TypeOf(0))) is.True(IsPredicate(func(int, string) bool { return false}, reflect.TypeOf(0), reflect.TypeOf(""))) is.False(IsPredicate(func(struct{}, string) bool { return false}, reflect.TypeOf(0), reflect.TypeOf(""))) is.True(IsPredicate(func(struct{}, string) bool { return false}, nil, reflect.TypeOf(""))) } go-funk-0.9.3/intersection.go000066400000000000000000000141021435240704200161250ustar00rootroot00000000000000package funk import ( "reflect" ) // Intersect returns the intersection between two collections. // // Deprecated: use Join(x, y, InnerJoin) instead of Intersect, InnerJoin // implements deduplication mechanism, so verify your code behaviour // before using it func Intersect(x interface{}, y interface{}) interface{} { if !IsCollection(x) { panic("First parameter must be a collection") } if !IsCollection(y) { panic("Second parameter must be a collection") } hash := map[interface{}]struct{}{} xValue := reflect.ValueOf(x) xType := xValue.Type() yValue := reflect.ValueOf(y) yType := yValue.Type() if NotEqual(xType, yType) { panic("Parameters must have the same type") } zType := reflect.SliceOf(xType.Elem()) zSlice := reflect.MakeSlice(zType, 0, 0) for i := 0; i < xValue.Len(); i++ { v := xValue.Index(i).Interface() hash[v] = struct{}{} } for i := 0; i < yValue.Len(); i++ { v := yValue.Index(i).Interface() _, ok := hash[v] if ok { zSlice = reflect.Append(zSlice, yValue.Index(i)) } } return zSlice.Interface() } // IntersectString returns the intersection between two collections of string. func IntersectString(x []string, y []string) []string { if len(x) == 0 || len(y) == 0 { return []string{} } set := []string{} hash := map[string]struct{}{} for _, v := range x { hash[v] = struct{}{} } for _, v := range y { _, ok := hash[v] if ok { set = append(set, v) } } return set } // Difference returns the difference between two collections. func Difference(x interface{}, y interface{}) (interface{}, interface{}) { if !IsIteratee(x) { panic("First parameter must be an iteratee") } if !IsIteratee(y) { panic("Second parameter must be an iteratee") } xValue := reflect.ValueOf(x) xType := xValue.Type() yValue := reflect.ValueOf(y) yType := yValue.Type() if NotEqual(xType, yType) { panic("Parameters must have the same type") } if xType.Kind() == reflect.Map { leftType := reflect.MapOf(xType.Key(), xType.Elem()) rightType := reflect.MapOf(xType.Key(), xType.Elem()) leftMap := reflect.MakeMap(leftType) rightMap := reflect.MakeMap(rightType) xIter := xValue.MapRange() for xIter.Next() { k := xIter.Key() xv := xIter.Value() yv := yValue.MapIndex(k) equalTo := equal(xv.Interface(), true) if !yv.IsValid() || !equalTo(yv, yv) { leftMap.SetMapIndex(k, xv) } } yIter := yValue.MapRange() for yIter.Next() { k := yIter.Key() yv := yIter.Value() xv := xValue.MapIndex(k) equalTo := equal(yv.Interface(), true) if !xv.IsValid() || !equalTo(xv, xv) { rightMap.SetMapIndex(k, yv) } } return leftMap.Interface(), rightMap.Interface() } else { leftType := reflect.SliceOf(xType.Elem()) rightType := reflect.SliceOf(yType.Elem()) leftSlice := reflect.MakeSlice(leftType, 0, 0) rightSlice := reflect.MakeSlice(rightType, 0, 0) for i := 0; i < xValue.Len(); i++ { v := xValue.Index(i).Interface() if !Contains(y, v) { leftSlice = reflect.Append(leftSlice, xValue.Index(i)) } } for i := 0; i < yValue.Len(); i++ { v := yValue.Index(i).Interface() if !Contains(x, v) { rightSlice = reflect.Append(rightSlice, yValue.Index(i)) } } return leftSlice.Interface(), rightSlice.Interface() } } // DifferenceString returns the difference between two collections of strings. func DifferenceString(x []string, y []string) ([]string, []string) { leftSlice := []string{} rightSlice := []string{} for _, v := range x { if !ContainsString(y, v) { leftSlice = append(leftSlice, v) } } for _, v := range y { if !ContainsString(x, v) { rightSlice = append(rightSlice, v) } } return leftSlice, rightSlice } // DifferenceInt64 returns the difference between two collections of int64s. func DifferenceInt64(x []int64, y []int64) ([]int64, []int64) { leftSlice := []int64{} rightSlice := []int64{} for _, v := range x { if !ContainsInt64(y, v) { leftSlice = append(leftSlice, v) } } for _, v := range y { if !ContainsInt64(x, v) { rightSlice = append(rightSlice, v) } } return leftSlice, rightSlice } // DifferenceInt32 returns the difference between two collections of ints32. func DifferenceInt32(x []int32, y []int32) ([]int32, []int32) { leftSlice := []int32{} rightSlice := []int32{} for _, v := range x { if !ContainsInt32(y, v) { leftSlice = append(leftSlice, v) } } for _, v := range y { if !ContainsInt32(x, v) { rightSlice = append(rightSlice, v) } } return leftSlice, rightSlice } // DifferenceInt returns the difference between two collections of ints. func DifferenceInt(x []int, y []int) ([]int, []int) { leftSlice := []int{} rightSlice := []int{} for _, v := range x { if !ContainsInt(y, v) { leftSlice = append(leftSlice, v) } } for _, v := range y { if !ContainsInt(x, v) { rightSlice = append(rightSlice, v) } } return leftSlice, rightSlice } // DifferenceUInt returns the difference between two collections of uints. func DifferenceUInt(x []uint, y []uint) ([]uint, []uint) { leftSlice := []uint{} rightSlice := []uint{} for _, v := range x { if !ContainsUInt(y, v) { leftSlice = append(leftSlice, v) } } for _, v := range y { if !ContainsUInt(x, v) { rightSlice = append(rightSlice, v) } } return leftSlice, rightSlice } // DifferenceUInt32 returns the difference between two collections of uints32. func DifferenceUInt32(x []uint32, y []uint32) ([]uint32, []uint32) { leftSlice := []uint32{} rightSlice := []uint32{} for _, v := range x { if !ContainsUInt32(y, v) { leftSlice = append(leftSlice, v) } } for _, v := range y { if !ContainsUInt32(x, v) { rightSlice = append(rightSlice, v) } } return leftSlice, rightSlice } // DifferenceUInt64 returns the difference between two collections of uints64. func DifferenceUInt64(x []uint64, y []uint64) ([]uint64, []uint64) { leftSlice := []uint64{} rightSlice := []uint64{} for _, v := range x { if !ContainsUInt64(y, v) { leftSlice = append(leftSlice, v) } } for _, v := range y { if !ContainsUInt64(x, v) { rightSlice = append(rightSlice, v) } } return leftSlice, rightSlice } go-funk-0.9.3/intersection_test.go000066400000000000000000000030471435240704200171720ustar00rootroot00000000000000package funk import ( "testing" "github.com/stretchr/testify/assert" ) func TestIntersect(t *testing.T) { is := assert.New(t) r := Intersect([]int{1, 2, 3, 4}, []int{2, 4, 6}) is.Equal(r, []int{2, 4}) r = Intersect([]string{"foo", "bar", "hello", "bar"}, []string{"foo", "bar"}) is.Equal(r, []string{"foo", "bar"}) r = Intersect([]string{"foo", "bar"}, []string{"foo", "bar", "hello", "bar"}) is.Equal(r, []string{"foo", "bar", "bar"}) } func TestIntersectString(t *testing.T) { is := assert.New(t) r := IntersectString([]string{"foo", "bar", "hello", "bar"}, []string{"foo", "bar"}) is.Equal(r, []string{"foo", "bar"}) } func TestDifference(t *testing.T) { is := assert.New(t) r1, r2 := Difference([]int{1, 2, 3, 4}, []int{2, 4, 6}) is.Equal(r1, []int{1, 3}) is.Equal(r2, []int{6}) r1, r2 = Difference([]string{"foo", "bar", "hello", "bar"}, []string{"foo", "bar"}) is.Equal(r1, []string{"hello"}) is.Equal(r2, []string{}) r1, r2 = Difference(map[string]string{"foo": "bar", "hello": "baz"}, map[string]string{"foo": "bar", "bar": "baz"}) is.Equal(r1, map[string]string{"hello": "baz"}) is.Equal(r2, map[string]string{"bar": "baz"}) } func TestDifferenceString(t *testing.T) { is := assert.New(t) r1, r2 := DifferenceString([]string{"foo", "bar", "hello", "bar"}, []string{"foo", "bar"}) is.Equal(r1, []string{"hello"}) is.Equal(r2, []string{}) } func TestDifferenceInt64(t *testing.T) { is := assert.New(t) r1, r2 := DifferenceInt64([]int64{1, 2, 3, 4}, []int64{4, 6}) is.Equal(r1, []int64{1, 2, 3}) is.Equal(r2, []int64{6}) } go-funk-0.9.3/join.go000066400000000000000000000055711435240704200143700ustar00rootroot00000000000000package funk import ( "reflect" "strings" ) type JoinFnc func(lx, rx reflect.Value) reflect.Value // Join combines two collections using the given join method. func Join(larr, rarr interface{}, fnc JoinFnc) interface{} { if !IsCollection(larr) { panic("First parameter must be a collection") } if !IsCollection(rarr) { panic("Second parameter must be a collection") } lvalue := reflect.ValueOf(larr) rvalue := reflect.ValueOf(rarr) if NotEqual(lvalue.Type(), rvalue.Type()) { panic("Parameters must have the same type") } return fnc(lvalue, rvalue).Interface() } // InnerJoin finds and returns matching data from two collections. func InnerJoin(lx, rx reflect.Value) reflect.Value { result := reflect.MakeSlice(reflect.SliceOf(lx.Type().Elem()), 0, lx.Len()+rx.Len()) rhash := hashSlice(rx) lhash := make(map[interface{}]struct{}, lx.Len()) for i := 0; i < lx.Len(); i++ { v := lx.Index(i) _, ok := rhash[v.Interface()] _, alreadyExists := lhash[v.Interface()] if ok && !alreadyExists { lhash[v.Interface()] = struct{}{} result = reflect.Append(result, v) } } return result } // OuterJoin finds and returns dissimilar data from two collections. func OuterJoin(lx, rx reflect.Value) reflect.Value { ljoin := LeftJoin(lx, rx) rjoin := RightJoin(lx, rx) result := reflect.MakeSlice(reflect.SliceOf(lx.Type().Elem()), ljoin.Len()+rjoin.Len(), ljoin.Len()+rjoin.Len()) for i := 0; i < ljoin.Len(); i++ { result.Index(i).Set(ljoin.Index(i)) } for i := 0; i < rjoin.Len(); i++ { result.Index(ljoin.Len() + i).Set(rjoin.Index(i)) } return result } // LeftJoin finds and returns dissimilar data from the first collection (left). func LeftJoin(lx, rx reflect.Value) reflect.Value { result := reflect.MakeSlice(reflect.SliceOf(lx.Type().Elem()), 0, lx.Len()) rhash := hashSlice(rx) for i := 0; i < lx.Len(); i++ { v := lx.Index(i) _, ok := rhash[v.Interface()] if !ok { result = reflect.Append(result, v) } } return result } // LeftJoin finds and returns dissimilar data from the second collection (right). func RightJoin(lx, rx reflect.Value) reflect.Value { return LeftJoin(rx, lx) } func hashSlice(arr reflect.Value) map[interface{}]struct{} { hash := map[interface{}]struct{}{} for i := 0; i < arr.Len(); i++ { v := arr.Index(i).Interface() hash[v] = struct{}{} } return hash } // StringerJoin joins an array of elements which implement the `String() string` function. // Direct copy of strings.Join() with a few tweaks. func StringerJoin(elems []interface{ String() string }, sep string) string { switch len(elems) { case 0: return "" case 1: return elems[0].String() } n := len(sep) * (len(elems) - 1) for i := 0; i < len(elems); i++ { n += len(elems[i].String()) } var b strings.Builder b.Grow(n) b.WriteString(elems[0].String()) for _, s := range elems[1:] { b.WriteString(sep) b.WriteString(s.String()) } return b.String() } go-funk-0.9.3/join_primitives.go000066400000000000000000000232171435240704200166400ustar00rootroot00000000000000package funk type JoinIntFnc func(lx, rx []int) []int // JoinInt combines two int collections using the given join method. func JoinInt(larr, rarr []int, fnc JoinIntFnc) []int { return fnc(larr, rarr) } // InnerJoinInt finds and returns matching data from two int collections. func InnerJoinInt(lx, rx []int) []int { result := make([]int, 0, len(lx)+len(rx)) rhash := hashSliceInt(rx) lhash := make(map[int]struct{}, len(lx)) for _, v := range lx { _, ok := rhash[v] _, alreadyExists := lhash[v] if ok && !alreadyExists { lhash[v] = struct{}{} result = append(result, v) } } return result } // OuterJoinInt finds and returns dissimilar data from two int collections. func OuterJoinInt(lx, rx []int) []int { ljoin := LeftJoinInt(lx, rx) rjoin := RightJoinInt(lx, rx) result := make([]int, len(ljoin)+len(rjoin)) copy(result, ljoin) for i, v := range rjoin { result[len(ljoin)+i] = v } return result } // LeftJoinInt finds and returns dissimilar data from the first int collection (left). func LeftJoinInt(lx, rx []int) []int { result := make([]int, 0, len(lx)) rhash := hashSliceInt(rx) for _, v := range lx { _, ok := rhash[v] if !ok { result = append(result, v) } } return result } // LeftJoinInt finds and returns dissimilar data from the second int collection (right). func RightJoinInt(lx, rx []int) []int { return LeftJoinInt(rx, lx) } func hashSliceInt(arr []int) map[int]struct{} { hash := make(map[int]struct{}, len(arr)) for _, i := range arr { hash[i] = struct{}{} } return hash } type JoinInt32Fnc func(lx, rx []int32) []int32 // JoinInt32 combines two int32 collections using the given join method. func JoinInt32(larr, rarr []int32, fnc JoinInt32Fnc) []int32 { return fnc(larr, rarr) } // InnerJoinInt32 finds and returns matching data from two int32 collections. func InnerJoinInt32(lx, rx []int32) []int32 { result := make([]int32, 0, len(lx)+len(rx)) rhash := hashSliceInt32(rx) lhash := make(map[int32]struct{}, len(lx)) for _, v := range lx { _, ok := rhash[v] _, alreadyExists := lhash[v] if ok && !alreadyExists { lhash[v] = struct{}{} result = append(result, v) } } return result } // OuterJoinInt32 finds and returns dissimilar data from two int32 collections. func OuterJoinInt32(lx, rx []int32) []int32 { ljoin := LeftJoinInt32(lx, rx) rjoin := RightJoinInt32(lx, rx) result := make([]int32, len(ljoin)+len(rjoin)) copy(result, ljoin) for i, v := range rjoin { result[len(ljoin)+i] = v } return result } // LeftJoinInt32 finds and returns dissimilar data from the first int32 collection (left). func LeftJoinInt32(lx, rx []int32) []int32 { result := make([]int32, 0, len(lx)) rhash := hashSliceInt32(rx) for _, v := range lx { _, ok := rhash[v] if !ok { result = append(result, v) } } return result } // LeftJoinInt32 finds and returns dissimilar data from the second int32 collection (right). func RightJoinInt32(lx, rx []int32) []int32 { return LeftJoinInt32(rx, lx) } func hashSliceInt32(arr []int32) map[int32]struct{} { hash := make(map[int32]struct{}, len(arr)) for _, i := range arr { hash[i] = struct{}{} } return hash } type JoinInt64Fnc func(lx, rx []int64) []int64 // JoinInt64 combines two int64 collections using the given join method. func JoinInt64(larr, rarr []int64, fnc JoinInt64Fnc) []int64 { return fnc(larr, rarr) } // InnerJoinInt64 finds and returns matching data from two int64 collections. func InnerJoinInt64(lx, rx []int64) []int64 { result := make([]int64, 0, len(lx)+len(rx)) rhash := hashSliceInt64(rx) lhash := make(map[int64]struct{}, len(lx)) for _, v := range lx { _, ok := rhash[v] _, alreadyExists := lhash[v] if ok && !alreadyExists { lhash[v] = struct{}{} result = append(result, v) } } return result } // OuterJoinInt64 finds and returns dissimilar data from two int64 collections. func OuterJoinInt64(lx, rx []int64) []int64 { ljoin := LeftJoinInt64(lx, rx) rjoin := RightJoinInt64(lx, rx) result := make([]int64, len(ljoin)+len(rjoin)) copy(result, ljoin) for i, v := range rjoin { result[len(ljoin)+i] = v } return result } // LeftJoinInt64 finds and returns dissimilar data from the first int64 collection (left). func LeftJoinInt64(lx, rx []int64) []int64 { result := make([]int64, 0, len(lx)) rhash := hashSliceInt64(rx) for _, v := range lx { _, ok := rhash[v] if !ok { result = append(result, v) } } return result } // LeftJoinInt64 finds and returns dissimilar data from the second int64 collection (right). func RightJoinInt64(lx, rx []int64) []int64 { return LeftJoinInt64(rx, lx) } func hashSliceInt64(arr []int64) map[int64]struct{} { hash := make(map[int64]struct{}, len(arr)) for _, i := range arr { hash[i] = struct{}{} } return hash } type JoinStringFnc func(lx, rx []string) []string // JoinString combines two string collections using the given join method. func JoinString(larr, rarr []string, fnc JoinStringFnc) []string { return fnc(larr, rarr) } // InnerJoinString finds and returns matching data from two string collections. func InnerJoinString(lx, rx []string) []string { result := make([]string, 0, len(lx)+len(rx)) rhash := hashSliceString(rx) lhash := make(map[string]struct{}, len(lx)) for _, v := range lx { _, ok := rhash[v] _, alreadyExists := lhash[v] if ok && !alreadyExists { lhash[v] = struct{}{} result = append(result, v) } } return result } // OuterJoinString finds and returns dissimilar data from two string collections. func OuterJoinString(lx, rx []string) []string { ljoin := LeftJoinString(lx, rx) rjoin := RightJoinString(lx, rx) result := make([]string, len(ljoin)+len(rjoin)) copy(result, ljoin) for i, v := range rjoin { result[len(ljoin)+i] = v } return result } // LeftJoinString finds and returns dissimilar data from the first string collection (left). func LeftJoinString(lx, rx []string) []string { result := make([]string, 0, len(lx)) rhash := hashSliceString(rx) for _, v := range lx { _, ok := rhash[v] if !ok { result = append(result, v) } } return result } // LeftJoinString finds and returns dissimilar data from the second string collection (right). func RightJoinString(lx, rx []string) []string { return LeftJoinString(rx, lx) } func hashSliceString(arr []string) map[string]struct{} { hash := make(map[string]struct{}, len(arr)) for _, i := range arr { hash[i] = struct{}{} } return hash } type JoinFloat32Fnc func(lx, rx []float32) []float32 // JoinFloat32 combines two float32 collections using the given join method. func JoinFloat32(larr, rarr []float32, fnc JoinFloat32Fnc) []float32 { return fnc(larr, rarr) } // InnerJoinFloat32 finds and returns matching data from two float32 collections. func InnerJoinFloat32(lx, rx []float32) []float32 { result := make([]float32, 0, len(lx)+len(rx)) rhash := hashSliceFloat32(rx) lhash := make(map[float32]struct{}, len(lx)) for _, v := range lx { _, ok := rhash[v] _, alreadyExists := lhash[v] if ok && !alreadyExists { lhash[v] = struct{}{} result = append(result, v) } } return result } // OuterJoinFloat32 finds and returns dissimilar data from two float32 collections. func OuterJoinFloat32(lx, rx []float32) []float32 { ljoin := LeftJoinFloat32(lx, rx) rjoin := RightJoinFloat32(lx, rx) result := make([]float32, len(ljoin)+len(rjoin)) copy(result, ljoin) for i, v := range rjoin { result[len(ljoin)+i] = v } return result } // LeftJoinFloat32 finds and returns dissimilar data from the first float32 collection (left). func LeftJoinFloat32(lx, rx []float32) []float32 { result := make([]float32, 0, len(lx)) rhash := hashSliceFloat32(rx) for _, v := range lx { _, ok := rhash[v] if !ok { result = append(result, v) } } return result } // LeftJoinFloat32 finds and returns dissimilar data from the second float32 collection (right). func RightJoinFloat32(lx, rx []float32) []float32 { return LeftJoinFloat32(rx, lx) } func hashSliceFloat32(arr []float32) map[float32]struct{} { hash := make(map[float32]struct{}, len(arr)) for _, i := range arr { hash[i] = struct{}{} } return hash } type JoinFloat64Fnc func(lx, rx []float64) []float64 // JoinFloat64 combines two float64 collections using the given join method. func JoinFloat64(larr, rarr []float64, fnc JoinFloat64Fnc) []float64 { return fnc(larr, rarr) } // InnerJoinFloat64 finds and returns matching data from two float64 collections. func InnerJoinFloat64(lx, rx []float64) []float64 { result := make([]float64, 0, len(lx)+len(rx)) rhash := hashSliceFloat64(rx) lhash := make(map[float64]struct{}, len(lx)) for _, v := range lx { _, ok := rhash[v] _, alreadyExists := lhash[v] if ok && !alreadyExists { lhash[v] = struct{}{} result = append(result, v) } } return result } // OuterJoinFloat64 finds and returns dissimilar data from two float64 collections. func OuterJoinFloat64(lx, rx []float64) []float64 { ljoin := LeftJoinFloat64(lx, rx) rjoin := RightJoinFloat64(lx, rx) result := make([]float64, len(ljoin)+len(rjoin)) copy(result, ljoin) for i, v := range rjoin { result[len(ljoin)+i] = v } return result } // LeftJoinFloat64 finds and returns dissimilar data from the first float64 collection (left). func LeftJoinFloat64(lx, rx []float64) []float64 { result := make([]float64, 0, len(lx)) rhash := hashSliceFloat64(rx) for _, v := range lx { _, ok := rhash[v] if !ok { result = append(result, v) } } return result } // LeftJoinFloat64 finds and returns dissimilar data from the second float64 collection (right). func RightJoinFloat64(lx, rx []float64) []float64 { return LeftJoinFloat64(rx, lx) } func hashSliceFloat64(arr []float64) map[float64]struct{} { hash := make(map[float64]struct{}, len(arr)) for _, i := range arr { hash[i] = struct{}{} } return hash } go-funk-0.9.3/join_test.go000066400000000000000000000061631435240704200154250ustar00rootroot00000000000000package funk import ( "fmt" "testing" "github.com/stretchr/testify/assert" ) func TestJoin_InnerJoin(t *testing.T) { testCases := []struct { LeftArr interface{} RightArr interface{} Expect interface{} }{ {[]string{"foo", "bar"}, []string{"bar", "baz"}, []string{"bar"}}, {[]string{"foo", "bar", "bar"}, []string{"bar", "baz"}, []string{"bar"}}, {[]string{"foo", "bar"}, []string{"bar", "bar", "baz"}, []string{"bar"}}, {[]string{"foo", "bar", "bar"}, []string{"bar", "bar", "baz"}, []string{"bar"}}, {[]int{0, 1, 2, 3, 4}, []int{3, 4, 5, 6, 7}, []int{3, 4}}, {[]*Foo{f, b}, []*Foo{b, c}, []*Foo{b}}, } for idx, tt := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) actual := Join(tt.LeftArr, tt.RightArr, InnerJoin) is.Equal(tt.Expect, actual) }) } } func TestJoin_OuterJoin(t *testing.T) { testCases := []struct { LeftArr interface{} RightArr interface{} Expect interface{} }{ {[]string{"foo", "bar"}, []string{"bar", "baz"}, []string{"foo", "baz"}}, {[]int{0, 1, 2, 3, 4}, []int{3, 4, 5, 6, 7}, []int{0, 1, 2, 5, 6, 7}}, {[]*Foo{f, b}, []*Foo{b, c}, []*Foo{f, c}}, } for idx, tt := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) actual := Join(tt.LeftArr, tt.RightArr, OuterJoin) is.Equal(tt.Expect, actual) }) } } func TestJoin_LeftJoin(t *testing.T) { testCases := []struct { LeftArr interface{} RightArr interface{} Expect interface{} }{ {[]string{"foo", "bar"}, []string{"bar", "baz"}, []string{"foo"}}, {[]int{0, 1, 2, 3, 4}, []int{3, 4, 5, 6, 7}, []int{0, 1, 2}}, {[]*Foo{f, b}, []*Foo{b, c}, []*Foo{f}}, } for idx, tt := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) actual := Join(tt.LeftArr, tt.RightArr, LeftJoin) is.Equal(tt.Expect, actual) }) } } func TestJoin_RightJoin(t *testing.T) { testCases := []struct { LeftArr interface{} RightArr interface{} Expect interface{} }{ {[]string{"foo", "bar"}, []string{"bar", "baz"}, []string{"baz"}}, {[]int{0, 1, 2, 3, 4}, []int{3, 4, 5, 6, 7}, []int{5, 6, 7}}, {[]*Foo{f, b}, []*Foo{b, c}, []*Foo{c}}, } for idx, tt := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) actual := Join(tt.LeftArr, tt.RightArr, RightJoin) is.Equal(tt.Expect, actual) }) } } // Struct which implements the String() method to test StringerJoin(). type S struct { Value string } func (s S) String() string { return s.Value } func TestJoin_StringerJoin(t *testing.T) { testCases := []struct { Arr []interface{ String() string } Sep string Expect string }{ {[]interface{ String() string }{}, ", ", ""}, {[]interface{ String() string }{S{"foo"}}, ", ", "foo"}, {[]interface{ String() string }{S{"foo"}, S{"bar"}, S{"baz"}}, ", ", "foo, bar, baz"}, } for idx, tt := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) actual := StringerJoin(tt.Arr, tt.Sep) is.Equal(tt.Expect, actual) }) } } go-funk-0.9.3/lazy_builder.go000066400000000000000000000071611435240704200161130ustar00rootroot00000000000000package funk import "reflect" type lazyBuilder struct { exec func() interface{} } func (b *lazyBuilder) Chunk(size int) Builder { return &lazyBuilder{func() interface{} { return Chunk(b.exec(), size) }} } func (b *lazyBuilder) Compact() Builder { return &lazyBuilder{func() interface{} { return Compact(b.exec()) }} } func (b *lazyBuilder) Drop(n int) Builder { return &lazyBuilder{func() interface{} { return Drop(b.exec(), n) }} } func (b *lazyBuilder) Filter(predicate interface{}) Builder { return &lazyBuilder{func() interface{} { return Filter(b.exec(), predicate) }} } func (b *lazyBuilder) Flatten() Builder { return &lazyBuilder{func() interface{} { return Flatten(b.exec()) }} } func (b *lazyBuilder) FlattenDeep() Builder { return &lazyBuilder{func() interface{} { return FlattenDeep(b.exec()) }} } func (b *lazyBuilder) Initial() Builder { return &lazyBuilder{func() interface{} { return Initial(b.exec()) }} } func (b *lazyBuilder) Intersect(y interface{}) Builder { return &lazyBuilder{func() interface{} { return Intersect(b.exec(), y) }} } func (b *lazyBuilder) Join(rarr interface{}, fnc JoinFnc) Builder { return &lazyBuilder{func() interface{} { return Join(b.exec(), rarr, fnc) }} } func (b *lazyBuilder) Map(mapFunc interface{}) Builder { return &lazyBuilder{func() interface{} { return Map(b.exec(), mapFunc) }} } func (b *lazyBuilder) FlatMap(mapFunc interface{}) Builder { return &lazyBuilder{func() interface{} { return FlatMap(b.exec(), mapFunc) }} } func (b *lazyBuilder) Reverse() Builder { return &lazyBuilder{func() interface{} { return Reverse(b.exec()) }} } func (b *lazyBuilder) Shuffle() Builder { return &lazyBuilder{func() interface{} { return Shuffle(b.exec()) }} } func (b *lazyBuilder) Tail() Builder { return &lazyBuilder{func() interface{} { return Tail(b.exec()) }} } func (b *lazyBuilder) Uniq() Builder { return &lazyBuilder{func() interface{} { return Uniq(b.exec()) }} } func (b *lazyBuilder) Without(values ...interface{}) Builder { return &lazyBuilder{func() interface{} { return Without(b.exec(), values...) }} } func (b *lazyBuilder) All() bool { return (&chainBuilder{b.exec()}).All() } func (b *lazyBuilder) Any() bool { return (&chainBuilder{b.exec()}).Any() } func (b *lazyBuilder) Contains(elem interface{}) bool { return Contains(b.exec(), elem) } func (b *lazyBuilder) Every(elements ...interface{}) bool { return Every(b.exec(), elements...) } func (b *lazyBuilder) Find(predicate interface{}) interface{} { return Find(b.exec(), predicate) } func (b *lazyBuilder) ForEach(predicate interface{}) { ForEach(b.exec(), predicate) } func (b *lazyBuilder) ForEachRight(predicate interface{}) { ForEachRight(b.exec(), predicate) } func (b *lazyBuilder) Head() interface{} { return Head(b.exec()) } func (b *lazyBuilder) Keys() interface{} { return Keys(b.exec()) } func (b *lazyBuilder) IndexOf(elem interface{}) int { return IndexOf(b.exec(), elem) } func (b *lazyBuilder) IsEmpty() bool { return IsEmpty(b.exec()) } func (b *lazyBuilder) Last() interface{} { return Last(b.exec()) } func (b *lazyBuilder) LastIndexOf(elem interface{}) int { return LastIndexOf(b.exec(), elem) } func (b *lazyBuilder) NotEmpty() bool { return NotEmpty(b.exec()) } func (b *lazyBuilder) Product() float64 { return Product(b.exec()) } func (b *lazyBuilder) Reduce(reduceFunc, acc interface{}) interface{} { return Reduce(b.exec(), reduceFunc, acc) } func (b *lazyBuilder) Sum() float64 { return Sum(b.exec()) } func (b *lazyBuilder) Type() reflect.Type { return reflect.TypeOf(b.exec()) } func (b *lazyBuilder) Value() interface{} { return b.exec() } func (b *lazyBuilder) Values() interface{} { return Values(b.exec()) } go-funk-0.9.3/lazy_builder_test.go000066400000000000000000000602451435240704200171540ustar00rootroot00000000000000package funk import ( "fmt" "reflect" "testing" "github.com/stretchr/testify/assert" ) func TestLazyChunk(t *testing.T) { testCases := []struct { In interface{} Size int }{ {In: []int{0, 1, 2, 3, 4}, Size: 2}, {In: []int{}, Size: 2}, {In: []int{1}, Size: 2}, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Chunk(tc.In, tc.Size) actual := LazyChain(tc.In).Chunk(tc.Size).Value() is.Equal(expected, actual) }) } } func TestLazyCompact(t *testing.T) { var emptyFunc func() bool emptyFuncPtr := &emptyFunc nonEmptyFunc := func() bool { return true } nonEmptyFuncPtr := &nonEmptyFunc nonEmptyMap := map[int]int{1: 2} nonEmptyMapPtr := &nonEmptyMap var emptyMap map[int]int emptyMapPtr := &emptyMap var emptyChan chan bool nonEmptyChan := make(chan bool, 1) nonEmptyChan <- true emptyChanPtr := &emptyChan nonEmptyChanPtr := &nonEmptyChan var emptyString string emptyStringPtr := &emptyString nonEmptyString := "42" nonEmptyStringPtr := &nonEmptyString testCases := []struct { In interface{} }{ {In: []interface{}{42, nil, (*int)(nil)}}, {In: []interface{}{42, emptyFuncPtr, emptyFunc, nonEmptyFuncPtr}}, {In: []interface{}{42, [2]int{}, map[int]int{}, []string{}, nonEmptyMapPtr, emptyMap, emptyMapPtr, nonEmptyMap, nonEmptyChan, emptyChan, emptyChanPtr, nonEmptyChanPtr}}, {In: []interface{}{true, 0, float64(0), "", "42", emptyStringPtr, nonEmptyStringPtr, false}}, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Compact(tc.In) actual := LazyChain(tc.In).Compact().Value() is.Equal(expected, actual) }) } } func TestLazyDrop(t *testing.T) { testCases := []struct { In interface{} N int }{ {In: []int{0, 1, 1, 2, 3, 0, 0, 12}, N: 3}, // Bug: Issues from go-funk (n parameter can be greater than len(in)) // {In: []int{0, 1}, N: 3}, // {In: []int{}, N: 3}, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Drop(tc.In, tc.N) actual := LazyChain(tc.In).Drop(tc.N).Value() is.Equal(expected, actual) }) } } func TestLazyFilter(t *testing.T) { testCases := []struct { In interface{} Predicate interface{} }{ { In: []int{1, 2, 3, 4}, Predicate: func(x int) bool { return x%2 == 0 }, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Filter(tc.In, tc.Predicate) actual := LazyChain(tc.In).Filter(tc.Predicate).Value() is.Equal(expected, actual) }) } } func TestLazyFilter_SideEffect(t *testing.T) { is := assert.New(t) type foo struct { bar string } in := []*foo{&foo{"foo"}, &foo{"bar"}} LazyChain := LazyChain(in) is.Equal([]*foo{&foo{"foo"}, &foo{"bar"}}, LazyChain.Value()) filtered := LazyChain.Filter(func(x *foo) bool { x.bar = "__" + x.bar + "__" return x.bar == "foo" }) is.Equal([]*foo{}, filtered.Value()) // Side effect: in and LazyChain.Value modified is.NotEqual([]*foo{&foo{"foo"}, &foo{"bar"}}, LazyChain.Value()) is.NotEqual([]*foo{&foo{"foo"}, &foo{"bar"}}, in) } func TestLazyFlatten(t *testing.T) { testCases := []struct { In interface{} }{ { In: [][]int{{1, 2}, {3, 4}}, }, { In: [][][]int{{{1, 2}, {3, 4}}, {{5, 6}}}, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Flatten(tc.In) actual := LazyChain(tc.In).Flatten().Value() is.Equal(expected, actual) }) } } func TestLazyFlattenDeep(t *testing.T) { testCases := []struct { In interface{} }{ { In: [][]int{{1, 2}, {3, 4}}, }, { In: [][][]int{{{1, 2}, {3, 4}}, {{5, 6}}}, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := FlattenDeep(tc.In) actual := LazyChain(tc.In).FlattenDeep().Value() is.Equal(expected, actual) }) } } func TestLazyInitial(t *testing.T) { testCases := []struct { In interface{} }{ { In: []int{}, }, { In: []int{0}, }, { In: []int{0, 1, 2, 3}, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Initial(tc.In) actual := LazyChain(tc.In).Initial().Value() is.Equal(expected, actual) }) } } func TestLazyIntersect(t *testing.T) { testCases := []struct { In interface{} Sec interface{} }{ { In: []int{1, 2, 3, 4}, Sec: []int{2, 4, 6}, }, { In: []string{"foo", "bar", "hello", "bar"}, Sec: []string{"foo", "bar"}, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Intersect(tc.In, tc.Sec) actual := LazyChain(tc.In).Intersect(tc.Sec).Value() is.Equal(expected, actual) }) } } func TestLazyMap(t *testing.T) { testCases := []struct { In interface{} MapFnc interface{} }{ { In: []int{1, 2, 3, 4}, MapFnc: func(x int) string { return "Hello" }, }, { In: []int{1, 2, 3, 4}, MapFnc: func(x int) (int, int) { return x, x }, }, { In: map[int]string{1: "Florent", 2: "Gilles"}, MapFnc: func(k int, v string) int { return k }, }, { In: map[int]string{1: "Florent", 2: "Gilles"}, MapFnc: func(k int, v string) (string, string) { return fmt.Sprintf("%d", k), v }, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Map(tc.In, tc.MapFnc) actual := LazyChain(tc.In).Map(tc.MapFnc).Value() if reflect.TypeOf(expected).Kind() == reflect.Map { is.Equal(expected, actual) } else { is.ElementsMatch(expected, actual) } }) } } func TestLazyFlatMap(t *testing.T) { testCases := []struct { In interface{} FlatMapFnc interface{} }{ { In: [][]int{{1}, {2}, {3}, {4}}, FlatMapFnc: func(x []int) []int { return x }, }, { In: map[string][]int{"Florent": {1}, "Gilles": {2}}, FlatMapFnc: func(k string, v []int) []int { return v }, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Map(tc.In, tc.FlatMapFnc) actual := LazyChain(tc.In).Map(tc.FlatMapFnc).Value() is.ElementsMatch(expected, actual) }) } } func TestLazyMap_SideEffect(t *testing.T) { is := assert.New(t) type foo struct { bar string } in := []*foo{&foo{"foo"}, &foo{"bar"}} LazyChain := LazyChain(in) is.Equal([]*foo{&foo{"foo"}, &foo{"bar"}}, LazyChain.Value()) mapped := LazyChain.Map(func(x *foo) (string, bool) { x.bar = "__" + x.bar + "__" return x.bar, x.bar == "foo" }) is.Equal(map[string]bool{"__foo__": false, "__bar__": false}, mapped.Value()) // Side effect: in and LazyChain.Value modified is.NotEqual([]*foo{&foo{"foo"}, &foo{"bar"}}, LazyChain.Value()) is.NotEqual([]*foo{&foo{"foo"}, &foo{"bar"}}, in) } func TestLazyReverse(t *testing.T) { testCases := []struct { In interface{} }{ { In: []int{0, 1, 2, 3, 4}, }, { In: "abcdefg", }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Reverse(tc.In) actual := LazyChain(tc.In).Reverse().Value() is.Equal(expected, actual) }) } } func TestLazyShuffle(t *testing.T) { testCases := []struct { In interface{} }{ { In: []int{0, 1, 2, 3, 4}, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Shuffle(tc.In) actual := LazyChain(tc.In).Shuffle().Value() is.NotEqual(expected, actual) is.ElementsMatch(expected, actual) }) } } func TestLazyTail(t *testing.T) { testCases := []struct { In interface{} }{ { In: []int{}, }, { In: []int{0}, }, { In: []int{0, 1, 2, 3}, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Tail(tc.In) actual := LazyChain(tc.In).Tail().Value() is.Equal(expected, actual) }) } } func TestLazyUniq(t *testing.T) { testCases := []struct { In interface{} }{ { In: []int{0, 1, 1, 2, 3, 0, 0, 12}, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Uniq(tc.In) actual := LazyChain(tc.In).Uniq().Value() is.Equal(expected, actual) }) } } func TestLazyAll(t *testing.T) { testCases := []struct { In []interface{} }{ {In: []interface{}{"foo", "bar"}}, {In: []interface{}{"foo", ""}}, {In: []interface{}{"", ""}}, {In: []interface{}{}}, {In: []interface{}{true, "foo", 6}}, {In: []interface{}{true, "", 6}}, {In: []interface{}{true, "foo", 0}}, {In: []interface{}{false, "foo", 6}}, {In: []interface{}{false, "", 0}}, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := All(tc.In...) actual := LazyChain(tc.In).All() is.Equal(expected, actual) }) } } func TestLazyAny(t *testing.T) { testCases := []struct { In []interface{} }{ {In: []interface{}{"foo", "bar"}}, {In: []interface{}{"foo", ""}}, {In: []interface{}{"", ""}}, {In: []interface{}{}}, {In: []interface{}{true, "foo", 6}}, {In: []interface{}{true, "", 6}}, {In: []interface{}{true, "foo", 0}}, {In: []interface{}{false, "foo", 6}}, {In: []interface{}{false, "", 0}}, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Any(tc.In...) actual := LazyChain(tc.In).Any() is.Equal(expected, actual) }) } } func TestLazyContains(t *testing.T) { testCases := []struct { In interface{} Contains interface{} }{ { In: []string{"foo", "bar"}, Contains: "bar", }, { In: []string{"foo", "bar"}, Contains: func (value string) bool { return value == "bar" }, }, { In: results, Contains: f, }, { In: results, Contains: nil, }, { In: results, Contains: b, }, { In: "florent", Contains: "rent", }, { In: "florent", Contains: "gilles", }, { In: map[int]*Foo{1: f, 3: c}, Contains: 1, }, { In: map[int]*Foo{1: f, 3: c}, Contains: 2, }, { In: map[int]*Foo{1: f, 3: c}, Contains: func (key int, foo *Foo) bool { return key == 3 && foo.FirstName == "Harald" }, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Contains(tc.In, tc.Contains) actual := LazyChain(tc.In).Contains(tc.Contains) is.Equal(expected, actual) }) } } func TestLazyEvery(t *testing.T) { testCases := []struct { In interface{} Contains []interface{} }{ { In: []string{"foo", "bar", "baz"}, Contains: []interface{}{"bar", "foo"}, }, { In: results, Contains: []interface{}{f, c}, }, { In: results, Contains: []interface{}{nil}, }, { In: results, Contains: []interface{}{f, b}, }, { In: "florent", Contains: []interface{}{"rent", "flo"}, }, { In: "florent", Contains: []interface{}{"rent", "gilles"}, }, { In: map[int]*Foo{1: f, 3: c}, Contains: []interface{}{1, 3}, }, { In: map[int]*Foo{1: f, 3: c}, Contains: []interface{}{2, 3}, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Every(tc.In, tc.Contains...) actual := LazyChain(tc.In).Every(tc.Contains...) is.Equal(expected, actual) }) } } func TestLazyFind(t *testing.T) { testCases := []struct { In interface{} Predicate interface{} }{ { In: []int{1, 2, 3, 4}, Predicate: func(x int) bool { return x%2 == 0 }, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Find(tc.In, tc.Predicate) actual := LazyChain(tc.In).Find(tc.Predicate) is.Equal(expected, actual) }) } } func TestLazyFind_SideEffect(t *testing.T) { is := assert.New(t) type foo struct { bar string } in := []*foo{&foo{"foo"}, &foo{"bar"}} LazyChain := LazyChain(in) is.Equal([]*foo{&foo{"foo"}, &foo{"bar"}}, LazyChain.Value()) result := LazyChain.Find(func(x *foo) bool { x.bar = "__" + x.bar + "__" return x.bar == "foo" }) is.Nil(result) // Side effect: in and LazyChain.Value modified is.NotEqual([]*foo{&foo{"foo"}, &foo{"bar"}}, LazyChain.Value()) is.NotEqual([]*foo{&foo{"foo"}, &foo{"bar"}}, in) } func TestLazyForEach(t *testing.T) { var expectedAcc, actualAcc []interface{} testCases := []struct { In interface{} FunkIterator interface{} LazyChainIterator interface{} }{ { In: []int{1, 2, 3, 4}, FunkIterator: func(x int) { if x%2 == 0 { expectedAcc = append(expectedAcc, x) } }, LazyChainIterator: func(x int) { if x%2 == 0 { actualAcc = append(actualAcc, x) } }, }, { In: map[int]string{1: "Florent", 2: "Gilles"}, FunkIterator: func(k int, v string) { expectedAcc = append(expectedAcc, fmt.Sprintf("%d:%s", k, v)) }, LazyChainIterator: func(k int, v string) { actualAcc = append(actualAcc, fmt.Sprintf("%d:%s", k, v)) }, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expectedAcc = []interface{}{} actualAcc = []interface{}{} ForEach(tc.In, tc.FunkIterator) LazyChain(tc.In).ForEach(tc.LazyChainIterator) is.ElementsMatch(expectedAcc, actualAcc) }) } } func TestLazyForEach_SideEffect(t *testing.T) { is := assert.New(t) type foo struct { bar string } var out []*foo in := []*foo{&foo{"foo"}, &foo{"bar"}} LazyChain := LazyChain(in) is.Equal([]*foo{&foo{"foo"}, &foo{"bar"}}, LazyChain.Value()) LazyChain.ForEach(func(x *foo) { x.bar = "__" + x.bar + "__" out = append(out, x) }) is.Equal([]*foo{&foo{"__foo__"}, &foo{"__bar__"}}, out) // Side effect: in and LazyChain.Value modified is.NotEqual([]*foo{&foo{"foo"}, &foo{"bar"}}, LazyChain.Value()) is.NotEqual([]*foo{&foo{"foo"}, &foo{"bar"}}, in) } func TestLazyForEachRight(t *testing.T) { var expectedAcc, actualAcc []interface{} testCases := []struct { In interface{} FunkIterator interface{} LazyChainIterator interface{} }{ { In: []int{1, 2, 3, 4}, FunkIterator: func(x int) { if x%2 == 0 { expectedAcc = append(expectedAcc, x) } }, LazyChainIterator: func(x int) { if x%2 == 0 { actualAcc = append(actualAcc, x) } }, }, { In: map[int]string{1: "Florent", 2: "Gilles"}, FunkIterator: func(k int, v string) { expectedAcc = append(expectedAcc, fmt.Sprintf("%d:%s", k, v)) }, LazyChainIterator: func(k int, v string) { actualAcc = append(actualAcc, fmt.Sprintf("%d:%s", k, v)) }, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expectedAcc = []interface{}{} actualAcc = []interface{}{} ForEachRight(tc.In, tc.FunkIterator) LazyChain(tc.In).ForEachRight(tc.LazyChainIterator) is.ElementsMatch(expectedAcc, actualAcc) }) } } func TestLazyForEachRight_SideEffect(t *testing.T) { is := assert.New(t) type foo struct { bar string } var out []*foo in := []*foo{&foo{"foo"}, &foo{"bar"}} LazyChain := LazyChain(in) is.Equal([]*foo{&foo{"foo"}, &foo{"bar"}}, LazyChain.Value()) LazyChain.ForEachRight(func(x *foo) { x.bar = "__" + x.bar + "__" out = append(out, x) }) is.Equal([]*foo{&foo{"__bar__"}, &foo{"__foo__"}}, out) // Side effect: in and LazyChain.Value modified is.NotEqual([]*foo{&foo{"foo"}, &foo{"bar"}}, LazyChain.Value()) is.NotEqual([]*foo{&foo{"foo"}, &foo{"bar"}}, in) } func TestLazyHead(t *testing.T) { testCases := []struct { In interface{} }{ { In: []int{1, 2, 3, 4}, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Head(tc.In) actual := LazyChain(tc.In).Head() is.Equal(expected, actual) }) } } func TestLazyKeys(t *testing.T) { testCases := []struct { In interface{} }{ {In: map[string]int{"one": 1, "two": 2}}, {In: &map[string]int{"one": 1, "two": 2}}, {In: map[int]complex128{5: 1 + 8i, 3: 2}}, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Keys(tc.In) actual := LazyChain(tc.In).Keys() is.ElementsMatch(expected, actual) }) } } func TestLazyIndexOf(t *testing.T) { testCases := []struct { In interface{} Item interface{} }{ { In: []string{"foo", "bar"}, Item: "bar", }, { In: []string{"foo", "bar"}, Item: func (value string) bool { return value == "bar" }, }, { In: results, Item: f, }, { In: results, Item: b, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := IndexOf(tc.In, tc.Item) actual := LazyChain(tc.In).IndexOf(tc.Item) is.Equal(expected, actual) }) } } func TestLazyIsEmpty(t *testing.T) { testCases := []struct { In interface{} }{ {In: ""}, {In: [0]interface{}{}}, {In: []interface{}(nil)}, {In: map[interface{}]interface{}(nil)}, {In: "s"}, {In: [1]interface{}{1}}, {In: []interface{}{}}, {In: map[interface{}]interface{}{}}, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := IsEmpty(tc.In) actual := LazyChain(tc.In).IsEmpty() is.Equal(expected, actual) }) } } func TestLazyLast(t *testing.T) { testCases := []struct { In interface{} }{ { In: []int{1, 2, 3, 4}, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Last(tc.In) actual := LazyChain(tc.In).Last() is.Equal(expected, actual) }) } } func TestLazyLastIndexOf(t *testing.T) { testCases := []struct { In interface{} Item interface{} }{ { In: []string{"foo", "bar", "bar"}, Item: "bar", }, { In: []string{"foo", "bar", "bar"}, Item: func (value string) bool { return value == "bar" }, }, { In: []int{1, 2, 2, 3}, Item: 2, }, { In: []int{1, 2, 2, 3}, Item: 4, }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := LastIndexOf(tc.In, tc.Item) actual := LazyChain(tc.In).LastIndexOf(tc.Item) is.Equal(expected, actual) }) } } func TestLazyNotEmpty(t *testing.T) { testCases := []struct { In interface{} }{ {In: ""}, {In: [0]interface{}{}}, {In: []interface{}(nil)}, {In: map[interface{}]interface{}(nil)}, {In: "s"}, {In: [1]interface{}{1}}, {In: []interface{}{}}, {In: map[interface{}]interface{}{}}, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := NotEmpty(tc.In) actual := LazyChain(tc.In).NotEmpty() is.Equal(expected, actual) }) } } func TestLazyProduct(t *testing.T) { testCases := []struct { In interface{} }{ {In: []int{0, 1, 2, 3}}, {In: &[]int{0, 1, 2, 3}}, {In: []interface{}{1, 2, 3, 0.5}}, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Product(tc.In) actual := LazyChain(tc.In).Product() is.Equal(expected, actual) }) } } func TestLazyReduce(t *testing.T) { testCases := []struct { In interface{} ReduceFunc interface{} Acc interface{} }{ { In: []int{1, 2, 3, 4}, ReduceFunc: func(acc, elem int) int { return acc + elem }, Acc: 0, }, { In: &[]int16{1, 2, 3, 4}, ReduceFunc: '+', Acc: 5, }, { In: []float64{1.1, 2.2, 3.3}, ReduceFunc: '+', Acc: 0, }, { In: &[]int{1, 2, 3, 5}, ReduceFunc: func(acc int8, elem int16) int32 { return int32(acc) * int32(elem) }, Acc: 1, }, { In: []interface{}{1, 2, 3.3, 4}, ReduceFunc: '*', Acc: 1, }, { In: []string{"1", "2", "3", "4"}, ReduceFunc: func(acc string, elem string) string { return acc + elem }, Acc: "", }, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Reduce(tc.In, tc.ReduceFunc, tc.Acc) actual := LazyChain(tc.In).Reduce(tc.ReduceFunc, tc.Acc) is.Equal(expected, actual) }) } } func TestLazySum(t *testing.T) { testCases := []struct { In interface{} }{ {In: []int{0, 1, 2, 3}}, {In: &[]int{0, 1, 2, 3}}, {In: []interface{}{1, 2, 3, 0.5}}, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Sum(tc.In) actual := LazyChain(tc.In).Sum() is.Equal(expected, actual) }) } } func TestLazyType(t *testing.T) { type key string var x key testCases := []struct { In interface{} }{ {In: []string{}}, {In: []int{}}, {In: []bool{}}, {In: []interface{}{}}, {In: &[]interface{}{}}, {In: map[int]string{}}, {In: map[complex128]int{}}, {In: map[string]string{}}, {In: map[int]interface{}{}}, {In: map[key]interface{}{}}, {In: &map[key]interface{}{}}, {In: ""}, {In: &x}, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) actual := LazyChain(tc.In).Type() is.Equal(reflect.TypeOf(tc.In), actual) }) } } func TestLazyValue(t *testing.T) { testCases := []struct { In interface{} }{ {In: []int{0, 1, 2, 3}}, {In: []string{"foo", "bar"}}, {In: &[]string{"foo", "bar"}}, {In: map[int]string{1: "foo", 2: "bar"}}, {In: map[string]string{"foo": "foo", "bar": "bar"}}, {In: &map[string]string{"foo": "foo", "bar": "bar"}}, {In: "foo"}, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) actual := LazyChain(tc.In).Value() is.Equal(tc.In, actual) }) } } func TestLazyValues(t *testing.T) { testCases := []struct { In interface{} }{ {In: map[string]int{"one": 1, "two": 2}}, {In: &map[string]int{"one": 1, "two": 2}}, {In: map[int]complex128{5: 1 + 8i, 3: 2}}, } for idx, tc := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) expected := Values(tc.In) actual := LazyChain(tc.In).Values() is.ElementsMatch(expected, actual) }) } } func TestComplexLazyChaining(t *testing.T) { is := assert.New(t) in := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} lazy := LazyChain(in) lazyWith := LazyChainWith(func() interface{} { return in }) // Without builder fa := Filter(in, func(x int) bool { return x%2 == 0 }) fb := Map(fa, func(x int) int { return x * 2 }) fc := Reverse(fa) // With lazy chaining la := lazy.Filter(func(x int) bool { return x%2 == 0 }) lb := la.Map(func(x int) int { return x * 2 }) lc := la.Reverse() // With lazy chaining with generator lwa := lazyWith.Filter(func(x int) bool { return x%2 == 0 }) lwb := lwa.Map(func(x int) int { return x * 2 }) lwc := lwa.Reverse() is.Equal(fa, la.Value()) is.Equal(fb, lb.Value()) is.Equal(fc, lc.Value()) is.Equal(fa, lwa.Value()) is.Equal(fb, lwb.Value()) is.Equal(fc, lwc.Value()) is.Equal(Contains(fb, 2), lb.Contains(2)) is.Equal(Contains(fb, 4), lb.Contains(4)) is.Equal(Sum(fb), lb.Sum()) is.Equal(Head(fb), lb.Head()) is.Equal(Head(fc), lc.Head()) is.Equal(Contains(fb, 2), lwb.Contains(2)) is.Equal(Contains(fb, 4), lwb.Contains(4)) is.Equal(Sum(fb), lwb.Sum()) is.Equal(Head(fb), lwb.Head()) is.Equal(Head(fc), lwc.Head()) } go-funk-0.9.3/map.go000066400000000000000000000030751435240704200142030ustar00rootroot00000000000000package funk import ( "fmt" "reflect" ) // Keys creates an array of the own enumerable map keys or struct field names. func Keys(out interface{}) interface{} { value := redirectValue(reflect.ValueOf(out)) valueType := value.Type() if value.Kind() == reflect.Map { keys := value.MapKeys() length := len(keys) resultSlice := reflect.MakeSlice(reflect.SliceOf(valueType.Key()), length, length) for i, key := range keys { resultSlice.Index(i).Set(key) } return resultSlice.Interface() } if value.Kind() == reflect.Struct { length := value.NumField() resultSlice := make([]string, length) for i := 0; i < length; i++ { resultSlice[i] = valueType.Field(i).Name } return resultSlice } panic(fmt.Sprintf("Type %s is not supported by Keys", valueType.String())) } // Values creates an array of the own enumerable map values or struct field values. func Values(out interface{}) interface{} { value := redirectValue(reflect.ValueOf(out)) valueType := value.Type() if value.Kind() == reflect.Map { keys := value.MapKeys() length := len(keys) resultSlice := reflect.MakeSlice(reflect.SliceOf(valueType.Elem()), length, length) for i, key := range keys { resultSlice.Index(i).Set(value.MapIndex(key)) } return resultSlice.Interface() } if value.Kind() == reflect.Struct { length := value.NumField() resultSlice := make([]interface{}, length) for i := 0; i < length; i++ { resultSlice[i] = value.Field(i).Interface() } return resultSlice } panic(fmt.Sprintf("Type %s is not supported by Keys", valueType.String())) } go-funk-0.9.3/map_test.go000066400000000000000000000013651435240704200152420ustar00rootroot00000000000000package funk import ( "sort" "testing" "github.com/stretchr/testify/assert" ) func TestKeys(t *testing.T) { is := assert.New(t) results := Keys(map[string]int{"one": 1, "two": 2}).([]string) sort.Strings(results) is.Equal(results, []string{"one", "two"}) fields := Keys(foo).([]string) sort.Strings(fields) is.Equal(fields, []string{"Age", "Bar", "BarInterface", "BarPointer", "Bars", "EmptyValue", "FirstName", "GeneralInterface", "ID", "LastName", "ZeroBoolValue", "ZeroIntPtrValue", "ZeroIntValue"}) } func TestValues(t *testing.T) { is := assert.New(t) results := Values(map[string]int{"one": 1, "two": 2}).([]int) sort.Ints(results) is.Equal(results, []int{1, 2}) values := Values(foo).([]interface{}) is.Len(values, 13) } go-funk-0.9.3/max.go000066400000000000000000000070171435240704200142130ustar00rootroot00000000000000package funk import "strings" // MaxInt validates the input, compares the elements and returns the maximum element in an array/slice. // It accepts []int // It returns int func MaxInt(i []int) int { if len(i) == 0 { panic("arg is an empty array/slice") } var max int for idx := 0; idx < len(i); idx++ { item := i[idx] if idx == 0 { max = item continue } if item > max { max = item } } return max } // MaxInt8 validates the input, compares the elements and returns the maximum element in an array/slice. // It accepts []int8 // It returns int8 func MaxInt8(i []int8) int8 { if len(i) == 0 { panic("arg is an empty array/slice") } var max int8 for idx := 0; idx < len(i); idx++ { item := i[idx] if idx == 0 { max = item continue } if item > max { max = item } } return max } // MaxInt16 validates the input, compares the elements and returns the maximum element in an array/slice. // It accepts []int16 // It returns int16 func MaxInt16(i []int16) int16 { if len(i) == 0 { panic("arg is an empty array/slice") } var max int16 for idx := 0; idx < len(i); idx++ { item := i[idx] if idx == 0 { max = item continue } if item > max { max = item } } return max } // MaxInt32 validates the input, compares the elements and returns the maximum element in an array/slice. // It accepts []int32 // It returns int32 func MaxInt32(i []int32) int32 { if len(i) == 0 { panic("arg is an empty array/slice") } var max int32 for idx := 0; idx < len(i); idx++ { item := i[idx] if idx == 0 { max = item continue } if item > max { max = item } } return max } // MaxInt64 validates the input, compares the elements and returns the maximum element in an array/slice. // It accepts []int64 // It returns int64 func MaxInt64(i []int64) int64 { if len(i) == 0 { panic("arg is an empty array/slice") } var max int64 for idx := 0; idx < len(i); idx++ { item := i[idx] if idx == 0 { max = item continue } if item > max { max = item } } return max } // MaxFloat32 validates the input, compares the elements and returns the maximum element in an array/slice. // It accepts []float32 // It returns float32 func MaxFloat32(i []float32) float32 { if len(i) == 0 { panic("arg is an empty array/slice") } var max float32 for idx := 0; idx < len(i); idx++ { item := i[idx] if idx == 0 { max = item continue } if item > max { max = item } } return max } // MaxFloat64 validates the input, compares the elements and returns the maximum element in an array/slice. // It accepts []float64 // It returns float64 func MaxFloat64(i []float64) float64 { if len(i) == 0 { panic("arg is an empty array/slice") } var max float64 for idx := 0; idx < len(i); idx++ { item := i[idx] if idx == 0 { max = item continue } if item > max { max = item } } return max } // MaxString validates the input, compares the elements and returns the maximum element in an array/slice. // It accepts []string // It returns string func MaxString(i []string) string { if len(i) == 0 { panic("arg is an empty array/slice") } var max string for idx := 0; idx < len(i); idx++ { item := i[idx] if idx == 0 { max = item continue } max = compareStringsMax(max, item) } return max } // compareStrings uses the strings.Compare method to compare two strings, and returns the greater one. func compareStringsMax(max, current string) string { r := strings.Compare(strings.ToLower(max), strings.ToLower(current)) if r > 0 { return max } return current } go-funk-0.9.3/max_test.go000066400000000000000000000047231435240704200152530ustar00rootroot00000000000000package funk import ( "testing" "github.com/stretchr/testify/assert" ) func TestMaxWithArrayNumericInput(t *testing.T) { //Test Data d1 := []int{8, 3, 4, 44, 0} n1 := []int{} d2 := []int8{3, 3, 5, 9, 1} n2 := []int8{} d3 := []int16{4, 5, 4, 33, 2} n3 := []int16{} d4 := []int32{5, 3, 21, 15, 3} n4 := []int32{} d5 := []int64{9, 3, 9, 1, 2} n5 := []int64{} //Calls r1 := MaxInt(d1) r2 := MaxInt8(d2) r3 := MaxInt16(d3) r4 := MaxInt32(d4) r5 := MaxInt64(d5) // Assertions assert.Equal(t, int(44), r1, "It should return the max value in array") assert.Panics(t, func() { MaxInt(n1) }, "It should panic") assert.Equal(t, int8(9), r2, "It should return the max value in array") assert.Panics(t, func() { MaxInt8(n2) }, "It should panic") assert.Equal(t, int16(33), r3, "It should return the max value in array") assert.Panics(t, func() { MaxInt16(n3) }, "It should panic") assert.Equal(t, int32(21), r4, "It should return the max value in array") assert.Panics(t, func() { MaxInt32(n4) }, "It should panic") assert.Equal(t, int64(9), r5, "It should return the max value in array") assert.Panics(t, func() { MaxInt64(n5) }, "It should panic") } func TestMaxWithArrayFloatInput(t *testing.T) { //Test Data d1 := []float64{2, 38.3, 4, 4.4, 4} n1 := []float64{} d2 := []float32{2.9, 1.3, 4.23, 4.4, 7.7} n2 := []float32{} //Calls r1 := MaxFloat64(d1) r2 := MaxFloat32(d2) // Assertions assert.Equal(t, float64(38.3), r1, "It should return the max value in array") assert.Panics(t, func() { MaxFloat64(n1) }, "It should panic") assert.Equal(t, float32(7.7), r2, "It should return the max value in array") assert.Panics(t, func() { MaxFloat32(n2) }, "It should panic") } func TestMaxWithArrayInputWithStrings(t *testing.T) { //Test Data d1 := []string{"abc", "abd", "cbd"} d2 := []string{"abc", "abd", "abe"} d3 := []string{"abc", "foo", " "} d4 := []string{"abc", "abc", "aaa"} n1 := []string{} //Calls r1 := MaxString(d1) r2 := MaxString(d2) r3 := MaxString(d3) r4 := MaxString(d4) // Assertions assert.Equal(t, "cbd", r1, "It should print cbd because its first char is max in the list") assert.Equal(t, "abe", r2, "It should print abe because its first different char is max in the list") assert.Equal(t, "foo", r3, "It should print foo because its first different char is max in the list") assert.Equal(t, "abc", r4, "It should print abc because its first different char is max in the list") assert.Panics(t, func() { MaxString(n1) }, "It should panic") } go-funk-0.9.3/min.go000066400000000000000000000066501435240704200142130ustar00rootroot00000000000000package funk import "strings" // MinInt validates the input, compares the elements and returns the minimum element in an array/slice. // It accepts []int // It returns int func MinInt(i []int) int { if len(i) == 0 { panic("arg is an empty array/slice") } var min int for idx := 0; idx < len(i); idx++ { item := i[idx] if idx == 0 { min = item continue } if item < min { min = item } } return min } // MinInt8 validates the input, compares the elements and returns the minimum element in an array/slice. // It accepts []int8 // It returns int8 func MinInt8(i []int8) int8 { if len(i) == 0 { panic("arg is an empty array/slice") } var min int8 for idx := 0; idx < len(i); idx++ { item := i[idx] if idx == 0 { min = item continue } if item < min { min = item } } return min } // MinInt16 validates the input, compares the elements and returns the minimum element in an array/slice. // It accepts []int16 // It returns int16 func MinInt16(i []int16) int16 { if len(i) == 0 { panic("arg is an empty array/slice") } var min int16 for idx := 0; idx < len(i); idx++ { item := i[idx] if idx == 0 { min = item continue } if item < min { min = item } } return min } // MinInt32 validates the input, compares the elements and returns the minimum element in an array/slice. // It accepts []int32 // It returns int32 func MinInt32(i []int32) int32 { if len(i) == 0 { panic("arg is an empty array/slice") } var min int32 for idx := 0; idx < len(i); idx++ { item := i[idx] if idx == 0 { min = item continue } if item < min { min = item } } return min } // MinInt64 validates the input, compares the elements and returns the minimum element in an array/slice. // It accepts []int64 // It returns int64 func MinInt64(i []int64) int64 { if len(i) == 0 { panic("arg is an empty array/slice") } var min int64 for idx := 0; idx < len(i); idx++ { item := i[idx] if idx == 0 { min = item continue } if item < min { min = item } } return min } // MinFloat32 validates the input, compares the elements and returns the minimum element in an array/slice. // It accepts []float32 // It returns float32 func MinFloat32(i []float32) float32 { if len(i) == 0 { panic("arg is an empty array/slice") } var min float32 for idx := 0; idx < len(i); idx++ { item := i[idx] if idx == 0 { min = item continue } if item < min { min = item } } return min } // MinFloat64 validates the input, compares the elements and returns the minimum element in an array/slice. // It accepts []float64 // It returns float64 func MinFloat64(i []float64) float64 { if len(i) == 0 { panic("arg is an empty array/slice") } var min float64 for idx := 0; idx < len(i); idx++ { item := i[idx] if idx == 0 { min = item continue } if item < min { min = item } } return min } // MinString validates the input, compares the elements and returns the minimum element in an array/slice. // It accepts []string // It returns string func MinString(i []string) string { if len(i) == 0 { panic("arg is an empty array/slice") } var min string for idx := 0; idx < len(i); idx++ { item := i[idx] if idx == 0 { min = item continue } min = compareStringsMin(min, item) } return min } func compareStringsMin(min, current string) string { r := strings.Compare(strings.ToLower(min), strings.ToLower(current)) if r < 0 { return min } return current } go-funk-0.9.3/min_test.go000066400000000000000000000047131435240704200152500ustar00rootroot00000000000000package funk import ( "testing" "github.com/stretchr/testify/assert" ) func TestMinWithArrayNumericInput(t *testing.T) { //Test Data d1 := []int{8, 3, 4, 44, 0} n1 := []int{} d2 := []int8{3, 3, 5, 9, 1} n2 := []int8{} d3 := []int16{4, 5, 4, 33, 2} n3 := []int16{} d4 := []int32{5, 3, 21, 15, 3} n4 := []int32{} d5 := []int64{9, 3, 9, 1, 2} n5 := []int64{} //Calls r1 := MinInt(d1) r2 := MinInt8(d2) r3 := MinInt16(d3) r4 := MinInt32(d4) r5 := MinInt64(d5) // Assertions assert.Equal(t, int(0), r1, "It should return the min value in array") assert.Panics(t, func() { MinInt(n1) }, "It should panic") assert.Equal(t, int8(1), r2, "It should return the min value in array") assert.Panics(t, func() { MinInt8(n2) }, "It should panic") assert.Equal(t, int16(2), r3, "It should return the min value in array") assert.Panics(t, func() { MinInt16(n3) }, "It should panic") assert.Equal(t, int32(3), r4, "It should return the min value in array") assert.Panics(t, func() { MinInt32(n4) }, "It should panic") assert.Equal(t, int64(1), r5, "It should return the min value in array") assert.Panics(t, func() { MinInt64(n5) }, "It should panic") } func TestMinWithArrayFloatInput(t *testing.T) { //Test Data d1 := []float64{2, 38.3, 4, 4.4, 4} n1 := []float64{} d2 := []float32{2.9, 1.3, 4.23, 4.4, 7.7} n2 := []float32{} //Calls r1 := MinFloat64(d1) r2 := MinFloat32(d2) // Assertions assert.Equal(t, float64(2), r1, "It should return the min value in array") assert.Panics(t, func() { MinFloat64(n1) }, "It should panic") assert.Equal(t, float32(1.3), r2, "It should return the min value in array") assert.Panics(t, func() { MinFloat32(n2) }, "It should panic") } func TestMinWithArrayInputWithStrings(t *testing.T) { //Test Data d1 := []string{"abc", "abd", "cbd"} d2 := []string{"abc", "abd", "abe"} d3 := []string{"abc", "foo", " "} d4 := []string{"abc", "abc", "aaa"} n1 := []string{} //Calls r1 := MinString(d1) r2 := MinString(d2) r3 := MinString(d3) r4 := MinString(d4) // Assertions assert.Equal(t, "abc", r1, "It should print cbd because its first char is min in the list") assert.Equal(t, "abc", r2, "It should print abe because its first different char is min in the list") assert.Equal(t, " ", r3, "It should print foo because its first different char is min in the list") assert.Equal(t, "aaa", r4, "It should print abc because its first different char is min in the list") assert.Panics(t, func() { MinString(n1) }, "It should panic") } go-funk-0.9.3/operation.go000066400000000000000000000023501435240704200154210ustar00rootroot00000000000000package funk import ( "fmt" "reflect" ) func calculate(arr interface{}, name string, operation rune) float64 { value := redirectValue(reflect.ValueOf(arr)) valueType := value.Type() kind := value.Kind() if kind == reflect.Array || kind == reflect.Slice { length := value.Len() if length == 0 { return 0 } result := map[rune]float64{ '+': 0.0, '*': 1, }[operation] for i := 0; i < length; i++ { elem := redirectValue(value.Index(i)).Interface() var value float64 switch e := elem.(type) { case int: value = float64(e) case int8: value = float64(e) case int16: value = float64(e) case int32: value = float64(e) case int64: value = float64(e) case float32: value = float64(e) case float64: value = e } switch operation { case '+': result += value case '*': result *= value } } return result } panic(fmt.Sprintf("Type %s is not supported by %s", valueType.String(), name)) } // Sum computes the sum of the values in array. func Sum(arr interface{}) float64 { return calculate(arr, "Sum", '+') } // Product computes the product of the values in array. func Product(arr interface{}) float64 { return calculate(arr, "Product", '*') } go-funk-0.9.3/operation_test.go000066400000000000000000000006761435240704200164710ustar00rootroot00000000000000package funk import ( "testing" "github.com/stretchr/testify/assert" ) func TestSum(t *testing.T) { is := assert.New(t) is.Equal(Sum([]int{1, 2, 3}), 6.0) is.Equal(Sum(&[]int{1, 2, 3}), 6.0) is.Equal(Sum([]interface{}{1, 2, 3, 0.5}), 6.5) } func TestProduct(t *testing.T) { is := assert.New(t) is.Equal(Product([]int{2, 3, 4}), 24.0) is.Equal(Product(&[]int{2, 3, 4}), 24.0) is.Equal(Product([]interface{}{2, 3, 4, 0.5}), 12.0) } go-funk-0.9.3/options.go000066400000000000000000000005511435240704200151150ustar00rootroot00000000000000package funk type options struct { allowZero bool } type option func(*options) func newOptions(values ...option) *options { opts := &options{ allowZero: false, } for _, o := range values { o(opts) } return opts } // WithAllowZero allows zero values. func WithAllowZero() func(*options) { return func(opts *options) { opts.allowZero = true } } go-funk-0.9.3/permutation.go000066400000000000000000000007601435240704200157730ustar00rootroot00000000000000package funk import "errors" // NextPermutation Implement next permutation, // which rearranges numbers into the lexicographically next greater permutation of numbers. func NextPermutation(nums []int) error { n := len(nums) if n == 0 { return errors.New("nums is empty") } i := n - 2 for i >= 0 && nums[i] >= nums[i+1] { i-- } if i >= 0 { j := n - 1 for j >= 0 && nums[i] >= nums[j] { j-- } nums[i], nums[j] = nums[j], nums[i] } ReverseInt(nums[i+1:]) return nil } go-funk-0.9.3/permutation_test.go000066400000000000000000000010571435240704200170320ustar00rootroot00000000000000package funk import ( "fmt" "testing" ) func TestNextPermutation(t *testing.T) { type args struct { nums []int } tests := []struct { name string args args wantErr bool }{ { name: "case1", args: args{ nums: []int{1, 2, 3}, }, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if err := NextPermutation(tt.args.nums); (err != nil) != tt.wantErr { t.Errorf("NextPermutation() error = %v, wantErr %v", err, tt.wantErr) } else { fmt.Println(tt.args.nums) } }) } } go-funk-0.9.3/predicate.go000066400000000000000000000027151435240704200153660ustar00rootroot00000000000000package funk import ( "reflect" ) // predicatesImpl contains the common implementation of AnyPredicates and AllPredicates. func predicatesImpl(value interface{}, wantedAnswer bool, predicates interface{}) bool { if !IsCollection(predicates) { panic("Predicates parameter must be an iteratee") } predicatesValue := reflect.ValueOf(predicates) inputValue := reflect.ValueOf(value) for i := 0; i < predicatesValue.Len(); i++ { funcValue := predicatesValue.Index(i) if !IsFunction(funcValue.Interface()) { panic("Got non function as predicate") } funcType := funcValue.Type() if !IsPredicate(funcValue.Interface()) { panic("Predicate function must have 1 parameter and must return boolean") } if !inputValue.Type().ConvertibleTo(funcType.In(0)) { panic("Given value is not compatible with type of parameter for the predicate.") } if result := funcValue.Call([]reflect.Value{inputValue}); wantedAnswer == result[0].Bool() { return wantedAnswer } } return !wantedAnswer } // AnyPredicates gets a value and a series of predicates, and return true if at least one of the predicates // is true. func AnyPredicates(value interface{}, predicates interface{}) bool { return predicatesImpl(value, true, predicates) } // AllPredicates gets a value and a series of predicates, and return true if all of the predicates are true. func AllPredicates(value interface{}, predicates interface{}) bool { return predicatesImpl(value, false, predicates) } go-funk-0.9.3/predicate_test.go000066400000000000000000000101741435240704200164230ustar00rootroot00000000000000package funk import ( "strings" "testing" "github.com/stretchr/testify/require" ) func TestAllPredicates(t *testing.T) { type args struct { value interface{} predicates interface{} } tests := []struct { name string args args want bool }{ { name: "Sanity string predicates", args: args{ value: "test", predicates: []func(string) bool{ func(v string) bool { return strings.Contains(v, "est") }, func(v string) bool { return len(v) < 10 }, func(v string) bool { return len(v) > 2 }, }, }, want: true, }, { name: "Sanity int predicates", args: args{ value: 4, predicates: []func(int) bool{ func(v int) bool { return v < 5 }, func(v int) bool { return v > 2 }, }, }, want: true, }, { name: "Failed predicate", args: args{ value: "test", predicates: []func(string) bool{ func(v string) bool { return strings.Contains(v, "est") }, func(v string) bool { return len(v) > 10 }, func(v string) bool { return len(v) > 2 }, }, }, want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := AllPredicates(tt.args.value, tt.args.predicates); got != tt.want { t.Errorf("AllPredicates() = %v, want %v", got, tt.want) } }) } } func TestAnyPredicates(t *testing.T) { type args struct { value interface{} predicates interface{} } tests := []struct { name string args args want bool }{ { name: "Sanity string predicates", args: args{ value: "test", predicates: []func(string) bool{ func(v string) bool { return strings.Contains(v, "est") }, func(v string) bool { return len(v) > 10 }, func(v string) bool { return len(v) < 2 }, }, }, want: true, }, { name: "Sanity int predicates", args: args{ value: 4, predicates: []func(int) bool{ func(v int) bool { return v > 5 }, func(v int) bool { return v > 2 }, }, }, want: true, }, { name: "All failed predicate", args: args{ value: "test", predicates: []func(string) bool{ func(v string) bool { return !strings.Contains(v, "est") }, func(v string) bool { return len(v) > 10 }, func(v string) bool { return len(v) < 2 }, }, }, want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := AnyPredicates(tt.args.value, tt.args.predicates); got != tt.want { t.Errorf("AnyPredicates() = %v, want %v", got, tt.want) } }) } } func TestPredicatesImplPanics(t *testing.T) { type args struct { value interface{} wantedAnswer bool predicates interface{} } tests := []struct { name string args args }{ { name: "predicates are not collection", args: args{ value: nil, wantedAnswer: false, predicates: nil, }, }, { name: "predicates are collection of strings", args: args{ value: nil, wantedAnswer: false, predicates: []string{"hey"}, }, }, { name: "predicate has 2 out parameters", args: args{ value: nil, wantedAnswer: false, predicates: []func(string) (bool, error){ func(string) (bool, error){return true, nil}}, }, }, { name: "predicate has out parameter of type string", args: args{ value: nil, wantedAnswer: false, predicates: []func(string) string{ func(string) string{return ""}}, }, }, { name: "predicate has 2 in parameters", args: args{ value: nil, wantedAnswer: false, predicates: []func(string, bool) bool{ func(string, bool) bool{return true}}, }, }, { name: "predicate has 0 in parameters", args: args{ value: nil, wantedAnswer: false, predicates: []func() bool{ func() bool{return true}}, }, }, { name: "value is not convertible to in parameter", args: args{ value: 1, wantedAnswer: false, predicates: []func(string) bool{ func(string) bool{return true}}, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { require.Panics(t, func() {predicatesImpl(tt.args.value, tt.args.wantedAnswer, tt.args.predicates)}) }) } } go-funk-0.9.3/presence.go000066400000000000000000000114001435240704200152210ustar00rootroot00000000000000package funk import ( "fmt" "reflect" "strings" ) // Filter iterates over elements of collection, returning an array of // all elements predicate returns truthy for. func Filter(arr interface{}, predicate interface{}) interface{} { if !IsIteratee(arr) { panic("First parameter must be an iteratee") } if !IsFunction(predicate, 1, 1) { panic("Second argument must be function") } funcValue := reflect.ValueOf(predicate) funcType := funcValue.Type() if funcType.Out(0).Kind() != reflect.Bool { panic("Return argument should be a boolean") } arrValue := reflect.ValueOf(arr) arrType := arrValue.Type() // Get slice type corresponding to array type resultSliceType := reflect.SliceOf(arrType.Elem()) // MakeSlice takes a slice kind type, and makes a slice. resultSlice := reflect.MakeSlice(resultSliceType, 0, 0) for i := 0; i < arrValue.Len(); i++ { elem := arrValue.Index(i) result := funcValue.Call([]reflect.Value{elem})[0].Interface().(bool) if result { resultSlice = reflect.Append(resultSlice, elem) } } return resultSlice.Interface() } // Find iterates over elements of collection, returning the first // element predicate returns truthy for. func Find(arr interface{}, predicate interface{}) interface{} { _, val := FindKey(arr, predicate) return val } // Find iterates over elements of collection, returning the first // element of an array and random of a map which predicate returns truthy for. func FindKey(arr interface{}, predicate interface{}) (matchKey, matchEle interface{}) { if !IsIteratee(arr) { panic("First parameter must be an iteratee") } if !IsFunction(predicate, 1, 1) { panic("Second argument must be function") } funcValue := reflect.ValueOf(predicate) funcType := funcValue.Type() if funcType.Out(0).Kind() != reflect.Bool { panic("Return argument should be a boolean") } arrValue := reflect.ValueOf(arr) var keyArrs []reflect.Value isMap := arrValue.Kind() == reflect.Map if isMap { keyArrs = arrValue.MapKeys() } for i := 0; i < arrValue.Len(); i++ { var ( elem reflect.Value key reflect.Value ) if isMap { key = keyArrs[i] elem = arrValue.MapIndex(key) } else { key = reflect.ValueOf(i) elem = arrValue.Index(i) } result := funcValue.Call([]reflect.Value{elem})[0].Interface().(bool) if result { return key.Interface(), elem.Interface() } } return nil, nil } // IndexOf gets the index at which the first occurrence of value is found in array or return -1 // if the value cannot be found func IndexOf(in interface{}, elem interface{}) int { inValue := reflect.ValueOf(in) elemValue := reflect.ValueOf(elem) inType := inValue.Type() if inType.Kind() == reflect.String { return strings.Index(inValue.String(), elemValue.String()) } if inType.Kind() == reflect.Slice { equalTo := equal(elem) for i := 0; i < inValue.Len(); i++ { if equalTo(reflect.Value{}, inValue.Index(i)) { return i } } } return -1 } // LastIndexOf gets the index at which the last occurrence of value is found in array or return -1 // if the value cannot be found func LastIndexOf(in interface{}, elem interface{}) int { inValue := reflect.ValueOf(in) elemValue := reflect.ValueOf(elem) inType := inValue.Type() if inType.Kind() == reflect.String { return strings.LastIndex(inValue.String(), elemValue.String()) } if inType.Kind() == reflect.Slice { length := inValue.Len() equalTo := equal(elem) for i := length - 1; i >= 0; i-- { if equalTo(reflect.Value{}, inValue.Index(i)) { return i } } } return -1 } // Contains returns true if an element is present in a iteratee. func Contains(in interface{}, elem interface{}) bool { inValue := reflect.ValueOf(in) elemValue := reflect.ValueOf(elem) inType := inValue.Type() switch inType.Kind() { case reflect.String: return strings.Contains(inValue.String(), elemValue.String()) case reflect.Map: equalTo := equal(elem, true) for _, key := range inValue.MapKeys() { if equalTo(key, inValue.MapIndex(key)) { return true } } case reflect.Slice, reflect.Array: equalTo := equal(elem) for i := 0; i < inValue.Len(); i++ { if equalTo(reflect.Value{}, inValue.Index(i)) { return true } } default: panic(fmt.Sprintf("Type %s is not supported by Contains, supported types are String, Map, Slice, Array", inType.String())) } return false } // Every returns true if every element is present in a iteratee. func Every(in interface{}, elements ...interface{}) bool { for _, elem := range elements { if !Contains(in, elem) { return false } } return true } // Some returns true if atleast one element is present in an iteratee. func Some(in interface{}, elements ...interface{}) bool { for _, elem := range elements { if Contains(in, elem) { return true } } return false } go-funk-0.9.3/presence_test.go000066400000000000000000000072041435240704200162670ustar00rootroot00000000000000package funk import ( "testing" "github.com/stretchr/testify/assert" ) var f = &Foo{ ID: 1, FirstName: "Dark", LastName: "Vador", Age: 30, Bar: &Bar{ Name: "Test", }, } var b = &Foo{ ID: 2, FirstName: "Florent", LastName: "Messa", Age: 28, } var c = &Foo{ ID: 3, FirstName: "Harald", LastName: "Nordgren", Age: 27, } var results = []*Foo{f, c} type Person struct { name string age int } func TestContains(t *testing.T) { is := assert.New(t) is.True(Contains([]string{"foo", "bar"}, "bar")) is.True(Contains([...]string{"foo", "bar"}, "bar")) is.Panics(func() { Contains(1, 2) }) is.True(Contains(results, f)) is.False(Contains(results, nil)) is.False(Contains(results, b)) is.True(Contains("florent", "rent")) is.False(Contains("florent", "gilles")) mapping := ToMap(results, "ID") is.True(Contains(mapping, 1)) is.False(Contains(mapping, 2)) is.False(Contains(mapping, func (key int, val *Foo) bool { return key == 4 })) is.True(Contains(mapping, func (key int, val *Foo) bool { return key == 1 })) is.False(Contains(mapping, func (_ int, val *Foo) bool { return val.FirstName == "NotPresent" })) is.True(Contains(mapping, func (_ int, val *Foo) bool { return val.FirstName == "Harald" })) } func TestEvery(t *testing.T) { is := assert.New(t) is.True(Every([]string{"foo", "bar", "baz"}, "bar", "foo")) is.True(Every(results, f, c)) is.False(Every(results, nil)) is.False(Every(results, f, b)) is.True(Every("florent", "rent", "flo")) is.False(Every("florent", "rent", "gilles")) mapping := ToMap(results, "ID") is.True(Every(mapping, 1, 3)) is.False(Every(mapping, 2, 3)) } func TestSome(t *testing.T) { is := assert.New(t) is.True(Some([]string{"foo", "bar", "baz"}, "foo")) is.True(Some([]string{"foo", "bar", "baz"}, "foo", "qux")) is.True(Some(results, f)) is.False(Some(results, b)) is.False(Some(results, nil)) is.True(Some(results, f, b)) is.True(Some("zeeshan", "zee", "tam")) is.False(Some("zeeshan", "zi", "tam")) persons := []Person{ Person{ name: "Zeeshan", age: 23, }, Person{ name: "Bob", age: 26, }, } person := Person{"Zeeshan", 23} person2 := Person{"Alice", 23} person3 := Person{"John", 26} is.True(Some(persons, person, person2)) is.False(Some(persons, person2, person3)) mapping := ToMap(results, "ID") is.True(Some(mapping, 1, 2)) is.True(Some(mapping, 4, 1)) is.False(Some(mapping, 4, 5)) } func TestIndexOf(t *testing.T) { is := assert.New(t) is.Equal(IndexOf([]string{"foo", "bar"}, "bar"), 1) is.Equal(IndexOf([]string{"foo", "bar"}, func (value string) bool { return value == "bar" }), 1) is.Equal(IndexOf(results, f), 0) is.Equal(IndexOf(results, b), -1) } func TestLastIndexOf(t *testing.T) { is := assert.New(t) is.Equal(LastIndexOf([]string{"foo", "bar", "bar"}, "bar"), 2) is.Equal(LastIndexOf([]string{"foo", "bar", "bar"}, func (value string) bool { return value == "bar" }), 2) is.Equal(LastIndexOf([]int{1, 2, 2, 3}, 2), 2) is.Equal(LastIndexOf([]int{1, 2, 2, 3}, 4), -1) } func TestFilter(t *testing.T) { is := assert.New(t) r := Filter([]int{1, 2, 3, 4}, func(x int) bool { return x%2 == 0 }) is.Equal(r, []int{2, 4}) } func TestFind(t *testing.T) { is := assert.New(t) r := Find([]int{1, 2, 3, 4}, func(x int) bool { return x%2 == 0 }) is.Equal(r, 2) } func TestFindKey(t *testing.T) { is := assert.New(t) k, r := FindKey(map[string]int{"a": 1, "b": 2, "c": 3, "d": 4}, func(x int) bool { return x == 2 }) is.Equal(r, 2) is.Equal(k, "b") k1, r1 := FindKey([]int{1, 2, 3, 4}, func(x int) bool { return x%2 == 0 }) is.Equal(r1, 2) is.Equal(k1, 1) } go-funk-0.9.3/reduce.go000066400000000000000000000043441435240704200146750ustar00rootroot00000000000000package funk import ( "reflect" ) // Reduce takes a collection and reduces it to a single value using a reduction // function (or a valid symbol) and an accumulator value. func Reduce(arr, reduceFunc, acc interface{}) interface{} { arrValue := redirectValue(reflect.ValueOf(arr)) if !IsIteratee(arrValue.Interface()) { panic("First parameter must be an iteratee") } returnType := reflect.TypeOf(Reduce).Out(0) isFunc := IsFunction(reduceFunc, 2, 1) isRune := reflect.TypeOf(reduceFunc).Kind() == reflect.Int32 if !(isFunc || isRune) { panic("Second argument must be a valid function or rune") } accValue := reflect.ValueOf(acc) sliceElemType := sliceElem(arrValue.Type()) if isRune { if arrValue.Kind() == reflect.Slice && sliceElemType.Kind() == reflect.Interface { accValue = accValue.Convert(returnType) } else { accValue = accValue.Convert(sliceElemType) } } else { accValue = accValue.Convert(reflect.TypeOf(reduceFunc).In(0)) } accType := accValue.Type() // Generate reduce function if was passed as rune if isRune { reduceSign := reduceFunc.(int32) if ok := map[rune]bool{'+': true, '*': true}[reduceSign]; !ok { panic("Invalid reduce sign, allowed: '+' and '*'") } in := []reflect.Type{accType, sliceElemType} out := []reflect.Type{accType} funcType := reflect.FuncOf(in, out, false) reduceFunc = reflect.MakeFunc(funcType, func(args []reflect.Value) []reflect.Value { acc := args[0].Interface() elem := args[1].Interface() var result float64 params := []interface{}{acc, elem} switch reduceSign { case '+': result = Sum(params) case '*': result = Product(params) } return []reflect.Value{reflect.ValueOf(result).Convert(accType)} }).Interface() } funcValue := reflect.ValueOf(reduceFunc) funcType := funcValue.Type() for i := 0; i < arrValue.Len(); i++ { if accType.ConvertibleTo(funcType.In(0)) { accValue = accValue.Convert(funcType.In(0)) } arrElementValue := arrValue.Index(i) if sliceElemType.ConvertibleTo(funcType.In(1)) { arrElementValue = arrElementValue.Convert(funcType.In(1)) } result := funcValue.Call([]reflect.Value{accValue, arrElementValue}) accValue = result[0] } return accValue.Convert(returnType).Interface() } go-funk-0.9.3/reduce_test.go000066400000000000000000000020761435240704200157340ustar00rootroot00000000000000package funk import ( "fmt" "testing" "github.com/stretchr/testify/assert" ) func TestReduce(t *testing.T) { testCases := []struct { Arr interface{} Func interface{} Acc interface{} Result interface{} }{ { []int{1, 2, 3, 4}, func(acc, elem float64) float64 { return acc + elem }, 0, float64(10), }, { &[]int16{1, 2, 3, 4}, '+', 5, int16(15), }, { []float64{1.1, 2.2, 3.3}, '+', 0, float64(6.6), }, { &[]int{1, 2, 3, 5}, func(acc int8, elem int16) int32 { return int32(acc) * int32(elem) }, 1, int32(30), }, { []interface{}{1, 2, 3.3, 4}, '*', 1, float64(26.4), }, { []string{"1", "2", "3", "4"}, func(acc string, elem string) string { return acc + elem }, "", "1234", }, } for idx, test := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) result := Reduce(test.Arr, test.Func, test.Acc) if !is.Equal(result, test.Result) { t.Errorf("%#v doesn't eqal to %#v", result, test.Result) } }) } } go-funk-0.9.3/retrieve.go000066400000000000000000000066321435240704200152550ustar00rootroot00000000000000package funk import ( "reflect" "strings" ) // Get retrieves the value from given path, retriever can be modified with available RetrieverOptions func Get(out interface{}, path string, opts ...option) interface{} { options := newOptions(opts...) result := get(reflect.ValueOf(out), path, opts...) // valid kind and we can return a result.Interface() without panic if result.Kind() != reflect.Invalid && result.CanInterface() { // if we don't allow zero and the result is a zero value return nil if !options.allowZero && result.IsZero() { return nil } // if the result kind is a pointer and its nil return nil if result.Kind() == reflect.Ptr && result.IsNil() { return nil } // return the result interface (i.e the zero value of it) return result.Interface() } return nil } // GetOrElse retrieves the value of the pointer or default. func GetOrElse(v interface{}, def interface{}) interface{} { val := reflect.ValueOf(v) if v == nil || (val.Kind() == reflect.Ptr && val.IsNil()) { return def } else if val.Kind() != reflect.Ptr { return v } return val.Elem().Interface() } func get(value reflect.Value, path string, opts ...option) reflect.Value { options := newOptions(opts...) if value.Kind() == reflect.Slice || value.Kind() == reflect.Array { var resultSlice reflect.Value length := value.Len() if length == 0 { zeroElement := reflect.Zero(value.Type().Elem()) pathValue := get(zeroElement, path) value = reflect.MakeSlice(reflect.SliceOf(pathValue.Type()), 0, 0) return value } for i := 0; i < length; i++ { item := value.Index(i) resultValue := get(item, path) if resultValue.Kind() == reflect.Invalid || (resultValue.IsZero() && !options.allowZero) { continue } resultType := resultValue.Type() if resultSlice.Kind() == reflect.Invalid { resultType := reflect.SliceOf(resultType) resultSlice = reflect.MakeSlice(resultType, 0, 0) } resultSlice = reflect.Append(resultSlice, resultValue) } // if the result is a slice of a slice, we need to flatten it if resultSlice.Kind() != reflect.Invalid && resultSlice.Type().Elem().Kind() == reflect.Slice { return flattenDeep(resultSlice) } return resultSlice } quoted := false parts := strings.FieldsFunc(path, func(r rune) bool { if r == '"' { quoted = !quoted } return !quoted && r == '.' }) for i, part := range parts { parts[i] = strings.Trim(part, "\"") } for _, part := range parts { value = redirectValue(value) kind := value.Kind() switch kind { case reflect.Invalid: continue case reflect.Struct: if isNilIndirection(value, part) { return reflect.ValueOf(nil) } value = value.FieldByName(part) case reflect.Map: value = value.MapIndex(reflect.ValueOf(part)) case reflect.Slice, reflect.Array: value = get(value, part) default: return reflect.ValueOf(nil) } } return value } func isNilIndirection(v reflect.Value, name string) bool { vType := v.Type() for i := 0; i < vType.NumField(); i++ { field := vType.Field(i) if !isEmbeddedStructPointerField(field) { return false } fieldType := field.Type.Elem() _, found := fieldType.FieldByName(name) if found { return v.Field(i).IsNil() } } return false } func isEmbeddedStructPointerField(field reflect.StructField) bool { if !field.Anonymous { return false } return field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct } go-funk-0.9.3/retrieve_test.go000066400000000000000000000073221435240704200163110ustar00rootroot00000000000000package funk import ( "testing" "github.com/stretchr/testify/assert" ) func TestGetSlice(t *testing.T) { is := assert.New(t) is.Equal(Get(SliceOf(foo), "ID"), []int{1}) is.Equal(Get(SliceOf(foo), "Bar.Name"), []string{"Test"}) is.Equal(Get(SliceOf(foo), "Bar"), []*Bar{bar}) is.Equal(Get(([]Foo)(nil), "Bar.Name"), []string{}) is.Equal(Get([]Foo{}, "Bar.Name"), []string{}) is.Equal(Get([]*Foo{}, "Bar.Name"), []string{}) } func TestGetSliceMultiLevel(t *testing.T) { is := assert.New(t) is.Equal(Get(foo, "Bar.Bars.Bar.Name"), []string{"Level2-1", "Level2-2"}) is.Equal(Get(SliceOf(foo), "Bar.Bars.Bar.Name"), []string{"Level2-1", "Level2-2"}) } func TestGetNull(t *testing.T) { is := assert.New(t) is.Equal(Get(foo, "EmptyValue.Int64"), int64(10)) is.Equal(Get(foo, "ZeroValue"), nil) is.Equal(false, Get(foo, "ZeroBoolValue", WithAllowZero())) is.Equal(nil, Get(fooUnexported, "unexported", WithAllowZero())) is.Equal(nil, Get(fooUnexported, "unexported", WithAllowZero())) is.Equal(Get(foo, "ZeroIntValue", WithAllowZero()), 0) is.Equal(Get(foo, "ZeroIntPtrValue", WithAllowZero()), nil) is.Equal(Get(foo, "EmptyValue.Int64", WithAllowZero()), int64(10)) is.Equal(Get(SliceOf(foo), "EmptyValue.Int64"), []int64{10}) } func TestGetNil(t *testing.T) { is := assert.New(t) is.Equal(Get(foo2, "Bar.Name"), nil) is.Equal(Get(foo2, "Bar.Name", WithAllowZero()), "") is.Equal(Get([]*Foo{foo, foo2}, "Bar.Name"), []string{"Test"}) is.Equal(Get([]*Foo{foo, foo2}, "Bar"), []*Bar{bar}) } func TestGetMap(t *testing.T) { is := assert.New(t) m := map[string]interface{}{ "bar": map[string]interface{}{ "name": "foobar", "example.com/hello": "world", }, } is.Equal("foobar", Get(m, "bar.name")) is.Equal("world", Get(m, `bar."example.com/hello"`)) is.Equal(nil, Get(m, "foo.name")) is.Equal(nil, Get(m, `foo."example.com/hello"`)) is.Equal([]interface{}{"dark", "dark"}, Get([]map[string]interface{}{m1, m2}, "firstname")) is.Equal([]interface{}{"test"}, Get([]map[string]interface{}{m1, m2}, "bar.name")) } func TestGetThroughInterface(t *testing.T) { is := assert.New(t) is.Equal(Get(foo, "BarInterface.Bars.Bar.Name"), []string{"Level2-1", "Level2-2"}) is.Equal(Get(foo, "BarPointer.Bars.Bar.Name"), []string{"Level2-1", "Level2-2"}) } func TestGetWithAllowZero(t *testing.T) { is := assert.New(t) var test []struct { Age int } for i := 0; i < 10; i++ { test = append(test, struct{ Age int }{Age: i}) } is.Equal(Get(test, "Age").([]int), []int{1, 2, 3, 4, 5, 6, 7, 8, 9}) is.Equal(Get(test, "Age", WithAllowZero()).([]int), []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}) } func TestGetNotFound(t *testing.T) { is := assert.New(t) is.Equal(nil, Get(foo, "id")) is.Equal(nil, Get(foo, "id.id")) is.Equal(nil, Get(foo, "Bar.id")) is.Equal(nil, Get(foo, "Bars.id")) } func TestGetSimple(t *testing.T) { is := assert.New(t) is.Equal(Get(foo, "ID"), 1) is.Equal(Get(foo, "Bar.Name"), "Test") result := Get(foo, "Bar.Bars.Name") is.Equal(result, []string{"Level1-1", "Level1-2"}) } func TestGetOrElse(t *testing.T) { is := assert.New(t) str := "hello world" is.Equal("hello world", GetOrElse(&str, "foobar")) is.Equal("hello world", GetOrElse(str, "foobar")) is.Equal("foobar", GetOrElse(nil, "foobar")) t.Run("nil with type", func(t *testing.T) { // test GetOrElse covers this case is.Equal("foobar", GetOrElse((*string)(nil), "foobar")) }) } func TestEmbeddedStructPointer(t *testing.T) { is := assert.New(t) root := RootStructPointer{} is.Equal(Get(root, "EmbeddedField"), nil) is.Equal(Get(root, "EmbeddedStruct.EmbeddedField"), nil) } func TestEmbeddedStructNotPointer(t *testing.T) { is := assert.New(t) root := RootStructNotPointer{} is.Equal(Get(root, "EmbeddedField"), nil) } go-funk-0.9.3/scan.go000066400000000000000000000124351435240704200143520ustar00rootroot00000000000000package funk import ( "fmt" "reflect" ) // ForEach iterates over elements of collection and invokes iteratee // for each element. func ForEach(arr interface{}, predicate interface{}) { if !IsIteratee(arr) { panic("First parameter must be an iteratee") } var ( funcValue = reflect.ValueOf(predicate) arrValue = reflect.ValueOf(arr) arrType = arrValue.Type() funcType = funcValue.Type() ) if arrType.Kind() == reflect.Slice || arrType.Kind() == reflect.Array { if !IsFunction(predicate, 1, 0) { panic("Second argument must be a function with one parameter") } arrElemType := arrValue.Type().Elem() arrElemPointerType := reflect.New(arrElemType).Type() usePointer := arrElemPointerType.ConvertibleTo(funcType.In(0)) // Checking whether element type is convertible to function's first argument's type. if !arrElemType.ConvertibleTo(funcType.In(0)) && !usePointer { panic("Map function's argument is not compatible with type of array.") } for i := 0; i < arrValue.Len(); i++ { if usePointer { funcValue.Call([]reflect.Value{arrValue.Index(i).Addr()}) } else { funcValue.Call([]reflect.Value{arrValue.Index(i)}) } } } if arrType.Kind() == reflect.Map { if !IsFunction(predicate, 2, 0) { panic("Second argument must be a function with two parameters") } // Type checking for Map = (key, value) keyType := arrType.Key() valueType := arrType.Elem() if !keyType.ConvertibleTo(funcType.In(0)) { panic(fmt.Sprintf("function first argument is not compatible with %s", keyType.String())) } if !valueType.ConvertibleTo(funcType.In(1)) { panic(fmt.Sprintf("function second argument is not compatible with %s", valueType.String())) } for _, key := range arrValue.MapKeys() { funcValue.Call([]reflect.Value{key, arrValue.MapIndex(key)}) } } } // ForEachRight iterates over elements of collection from the right and invokes iteratee // for each element. func ForEachRight(arr interface{}, predicate interface{}) { if !IsIteratee(arr) { panic("First parameter must be an iteratee") } var ( funcValue = reflect.ValueOf(predicate) arrValue = reflect.ValueOf(arr) arrType = arrValue.Type() funcType = funcValue.Type() ) if arrType.Kind() == reflect.Slice || arrType.Kind() == reflect.Array { if !IsFunction(predicate, 1, 0) { panic("Second argument must be a function with one parameter") } arrElemType := arrValue.Type().Elem() arrElemPointerType := reflect.New(arrElemType).Type() usePointer := arrElemPointerType.ConvertibleTo(funcType.In(0)) // Checking whether element type is convertible to function's first argument's type. if !arrElemType.ConvertibleTo(funcType.In(0)) && !usePointer { panic("Map function's argument is not compatible with type of array.") } for i := arrValue.Len() - 1; i >= 0; i-- { if usePointer { funcValue.Call([]reflect.Value{arrValue.Index(i).Addr()}) } else { funcValue.Call([]reflect.Value{arrValue.Index(i)}) } } } if arrType.Kind() == reflect.Map { if !IsFunction(predicate, 2, 0) { panic("Second argument must be a function with two parameters") } // Type checking for Map = (key, value) keyType := arrType.Key() valueType := arrType.Elem() if !keyType.ConvertibleTo(funcType.In(0)) { panic(fmt.Sprintf("function first argument is not compatible with %s", keyType.String())) } if !valueType.ConvertibleTo(funcType.In(1)) { panic(fmt.Sprintf("function second argument is not compatible with %s", valueType.String())) } keys := Reverse(arrValue.MapKeys()).([]reflect.Value) for _, key := range keys { funcValue.Call([]reflect.Value{key, arrValue.MapIndex(key)}) } } } // Head gets the first element of array. func Head(arr interface{}) interface{} { value := redirectValue(reflect.ValueOf(arr)) valueType := value.Type() kind := value.Kind() if kind == reflect.Array || kind == reflect.Slice { if value.Len() == 0 { return nil } return value.Index(0).Interface() } panic(fmt.Sprintf("Type %s is not supported by Head", valueType.String())) } // Last gets the last element of array. func Last(arr interface{}) interface{} { value := redirectValue(reflect.ValueOf(arr)) valueType := value.Type() kind := value.Kind() if kind == reflect.Array || kind == reflect.Slice { if value.Len() == 0 { return nil } return value.Index(value.Len() - 1).Interface() } panic(fmt.Sprintf("Type %s is not supported by Last", valueType.String())) } // Initial gets all but the last element of array. func Initial(arr interface{}) interface{} { value := redirectValue(reflect.ValueOf(arr)) valueType := value.Type() kind := value.Kind() if kind == reflect.Array || kind == reflect.Slice { length := value.Len() if length <= 1 { return arr } return value.Slice(0, length-1).Interface() } panic(fmt.Sprintf("Type %s is not supported by Initial", valueType.String())) } // Tail gets all but the first element of array. func Tail(arr interface{}) interface{} { value := redirectValue(reflect.ValueOf(arr)) valueType := value.Type() kind := value.Kind() if kind == reflect.Array || kind == reflect.Slice { length := value.Len() if length <= 1 { return arr } return value.Slice(1, length).Interface() } panic(fmt.Sprintf("Type %s is not supported by Initial", valueType.String())) } go-funk-0.9.3/scan_test.go000066400000000000000000000041131435240704200154030ustar00rootroot00000000000000package funk import ( "testing" "github.com/stretchr/testify/assert" ) func TestForEach(t *testing.T) { is := assert.New(t) results := []int{} ForEach([]int{1, 2, 3, 4}, func(x int) { if x%2 == 0 { results = append(results, x) } }) is.Equal(results, []int{2, 4}) toModify := []int{1, 2, 3} ForEach(toModify, func(x *int) { *x = *x * 2 }) is.Equal(toModify, []int{2, 4, 6}) toModify = []int{} ForEach(toModify, func(x *int) {}) is.Equal(toModify, []int{}) strModify := []string{"a", "b"} ForEach(strModify, func(s *string) { *s = *s + *s }) is.Equal(strModify, []string{"aa", "bb"}) mapping := map[int]string{ 1: "Florent", 2: "Gilles", } ForEach(mapping, func(k int, v string) { is.Equal(v, mapping[k]) }) } func TestForEachRight(t *testing.T) { is := assert.New(t) results := []int{} ForEachRight([]int{1, 2, 3, 4}, func(x int) { results = append(results, x*2) }) is.Equal(results, []int{8, 6, 4, 2}) toModify := []int{1, 2, 3} ForEach(toModify, func(x *int) { *x = *x * 2 }) is.Equal(toModify, []int{2, 4, 6}) toModify = []int{} ForEach(toModify, func(x *int) {}) is.Equal(toModify, []int{}) strModify := []string{"a", "b"} ForEach(strModify, func(s *string) { *s = *s + *s }) is.Equal(strModify, []string{"aa", "bb"}) mapping := map[int]string{ 1: "Florent", 2: "Gilles", } mapKeys := []int{} ForEachRight(mapping, func(k int, v string) { is.Equal(v, mapping[k]) mapKeys = append(mapKeys, k) }) is.Equal(len(mapKeys), 2) is.Contains(mapKeys, 1) is.Contains(mapKeys, 2) } func TestHead(t *testing.T) { is := assert.New(t) is.Equal(Head([]int{1, 2, 3, 4}), 1) } func TestLast(t *testing.T) { is := assert.New(t) is.Equal(Last([]int{1, 2, 3, 4}), 4) } func TestTail(t *testing.T) { is := assert.New(t) is.Equal(Tail([]int{}), []int{}) is.Equal(Tail([]int{1}), []int{1}) is.Equal(Tail([]int{1, 2, 3, 4}), []int{2, 3, 4}) } func TestInitial(t *testing.T) { is := assert.New(t) is.Equal(Initial([]int{}), []int{}) is.Equal(Initial([]int{1}), []int{1}) is.Equal(Initial([]int{1, 2, 3, 4}), []int{1, 2, 3}) } go-funk-0.9.3/short_if.go000066400000000000000000000002011435240704200152270ustar00rootroot00000000000000package funk func ShortIf(condition bool, a interface{}, b interface{}) interface{} { if condition { return a } return b } go-funk-0.9.3/short_if_test.go000066400000000000000000000010121435240704200162670ustar00rootroot00000000000000package funk import ( "testing" "github.com/stretchr/testify/assert" ) func TestShortIf(t *testing.T) { is := assert.New(t) r := ShortIf(10 > 5, 10, 5) is.Equal(r, 10) r = ShortIf(10.0 == 10, "yes", "no") is.Equal(r, "yes") r = ShortIf('a' == 'b', "equal chars", "unequal chars") is.Equal(r, "unequal chars") r = ShortIf(true, "Same string", "Different strings") is.Equal(r, "Same string") type testStruct struct{} a := testStruct{} b := testStruct{} r = ShortIf(a == b, &a, &b) is.Equal(r, &b) } go-funk-0.9.3/subset.go000066400000000000000000000013471435240704200147330ustar00rootroot00000000000000package funk import ( "reflect" ) // Subset returns true if collection x is a subset of y. func Subset(x interface{}, y interface{}) bool { if !IsCollection(x) { panic("First parameter must be a collection") } if !IsCollection(y) { panic("Second parameter must be a collection") } xValue := reflect.ValueOf(x) xType := xValue.Type() yValue := reflect.ValueOf(y) yType := yValue.Type() if NotEqual(xType, yType) { panic("Parameters must have the same type") } if xValue.Len() == 0 { return true } if yValue.Len() == 0 || yValue.Len() < xValue.Len() { return false } for i := 0; i < xValue.Len(); i++ { if !Contains(yValue.Interface(), xValue.Index(i).Interface()) { return false } } return true } go-funk-0.9.3/subset_test.go000066400000000000000000000014341435240704200157670ustar00rootroot00000000000000package funk import ( "testing" "github.com/stretchr/testify/assert" ) func TestSubset(t *testing.T) { is := assert.New(t) r := Subset([]int{1, 2, 4}, []int{1, 2, 3, 4, 5}) is.True(r) r = Subset([]string{"foo", "bar"},[]string{"foo", "bar", "hello", "bar", "hi"}) is.True(r) r = Subset([]string{"foo", "bar","extra"},[]string{"foo", "bar", "hello", "bar", "hi"}) is.False(r) r = Subset([]string{"hello", "foo", "bar", "hello", "bar", "hi"}, []string{}) is.False(r) r = Subset([]string{}, []string{"hello", "foo", "bar", "hello", "bar", "hi"}) is.True(r) r = Subset([]string{}, []string{}) is.True(r) r = Subset([]string{}, []string{"hello"}) is.True(r) r = Subset([]string{"hello", "foo", "bar", "hello", "bar", "hi"}, []string{"foo", "bar"} ) is.False(r) } go-funk-0.9.3/subtraction.go000066400000000000000000000027651435240704200157700ustar00rootroot00000000000000package funk import ( "reflect" ) // Subtract returns the subtraction between two collections. func Subtract(x interface{}, y interface{}) interface{} { if !IsCollection(x) { panic("First parameter must be a collection") } if !IsCollection(y) { panic("Second parameter must be a collection") } hash := map[interface{}]struct{}{} xValue := reflect.ValueOf(x) xType := xValue.Type() yValue := reflect.ValueOf(y) yType := yValue.Type() if NotEqual(xType, yType) { panic("Parameters must have the same type") } zType := reflect.SliceOf(xType.Elem()) zSlice := reflect.MakeSlice(zType, 0, 0) for i := 0; i < xValue.Len(); i++ { v := xValue.Index(i).Interface() hash[v] = struct{}{} } for i := 0; i < yValue.Len(); i++ { v := yValue.Index(i).Interface() _, ok := hash[v] if ok { delete(hash, v) } } for i := 0; i < xValue.Len(); i++ { v := xValue.Index(i).Interface() _, ok := hash[v] if ok { zSlice = reflect.Append(zSlice, xValue.Index(i)) } } return zSlice.Interface() } // SubtractString returns the subtraction between two collections of string func SubtractString(x []string, y []string) []string { if len(x) == 0 { return []string{} } if len(y) == 0 { return x } slice := []string{} hash := map[string]struct{}{} for _, v := range x { hash[v] = struct{}{} } for _, v := range y { _, ok := hash[v] if ok { delete(hash, v) } } for _, v := range x { _, ok := hash[v] if ok { slice = append(slice, v) } } return slice } go-funk-0.9.3/subtraction_test.go000066400000000000000000000022101435240704200170100ustar00rootroot00000000000000package funk import ( "testing" "github.com/stretchr/testify/assert" ) func TestSubtract(t *testing.T) { is := assert.New(t) r := Subtract([]int{1, 2, 3, 4, 5}, []int{2, 4, 6}) is.Equal([]int{1, 3, 5}, r) r = Subtract([]string{"foo", "bar", "hello", "bar", "hi"}, []string{"foo", "bar"}) is.Equal([]string{"hello", "hi"}, r) r = Subtract([]string{"hello", "foo", "bar", "hello", "bar", "hi"}, []string{"foo", "bar"}) is.Equal([]string{"hello", "hello", "hi"}, r) r = Subtract([]int{1, 2, 3, 4, 5}, []int{}) is.Equal([]int{1, 2, 3, 4, 5}, r) r = Subtract([]string{"bar", "bar"}, []string{}) is.Equal([]string{"bar", "bar"}, r) } func TestSubtractString(t *testing.T) { is := assert.New(t) r := SubtractString([]string{"foo", "bar", "hello", "bar"}, []string{"foo", "bar"}) is.Equal([]string{"hello"}, r) r = SubtractString([]string{"foo", "bar", "hello", "bar", "world", "world"}, []string{"foo", "bar"}) is.Equal([]string{"hello", "world", "world"}, r) r = SubtractString([]string{"bar", "bar"}, []string{}) is.Equal([]string{"bar", "bar"}, r) r = SubtractString([]string{}, []string{"bar", "bar"}) is.Equal([]string{}, r) } go-funk-0.9.3/transform.go000066400000000000000000000362451435240704200154460ustar00rootroot00000000000000package funk import ( "fmt" "math/rand" "reflect" "strings" ) // Chunk creates an array of elements split into groups with the length of size. // If array can't be split evenly, the final chunk will be // the remaining element. func Chunk(arr interface{}, size int) interface{} { if !IsIteratee(arr) { panic("First parameter must be neither array nor slice") } if size == 0 { return arr } arrValue := reflect.ValueOf(arr) arrType := arrValue.Type() resultSliceType := reflect.SliceOf(arrType) // Initialize final result slice which will contains slice resultSlice := reflect.MakeSlice(resultSliceType, 0, 0) itemType := arrType.Elem() var itemSlice reflect.Value itemSliceType := reflect.SliceOf(itemType) length := arrValue.Len() for i := 0; i < length; i++ { if i%size == 0 || i == 0 { if itemSlice.Kind() != reflect.Invalid { resultSlice = reflect.Append(resultSlice, itemSlice) } itemSlice = reflect.MakeSlice(itemSliceType, 0, 0) } itemSlice = reflect.Append(itemSlice, arrValue.Index(i)) if i == length-1 { resultSlice = reflect.Append(resultSlice, itemSlice) } } return resultSlice.Interface() } // ToMap transforms a collection of instances to a Map. // []T => map[type of T.]T func ToMap(in interface{}, pivot string) interface{} { // input value must be a collection if !IsCollection(in) { panic(fmt.Sprintf("%v must be a slict or an array", in)) } value := reflect.ValueOf(in) inType := value.Type() structType := inType.Elem() // retrieve the struct in the slice to deduce key type if structType.Kind() == reflect.Ptr { structType = structType.Elem() } field, ok := structType.FieldByName(pivot) if !ok { panic(fmt.Sprintf("`%s` must be a field of the struct %s", pivot, structType.Name())) } // value of the map will be the input type collectionType := reflect.MapOf(field.Type, inType.Elem()) // create a map from scratch collection := reflect.MakeMap(collectionType) for i := 0; i < value.Len(); i++ { instance := value.Index(i) var field reflect.Value if instance.Kind() == reflect.Ptr { field = instance.Elem().FieldByName(pivot) } else { field = instance.FieldByName(pivot) } collection.SetMapIndex(field, instance) } return collection.Interface() } // ToSet transforms a collection of instances to a Set. // []T => map[T]struct{} func ToSet(in interface{}) interface{} { // input value must be a collection if !IsCollection(in) { panic(fmt.Sprintf("%v must be a slice or an array", in)) } var ( empty = struct{}{} emptyType = reflect.TypeOf(empty) emptyValue = reflect.ValueOf(empty) ) value := reflect.ValueOf(in) elemType := value.Type().Elem() // key of the set will be the input type collection := reflect.MakeMap(reflect.MapOf(elemType, emptyType)) for i := 0; i < value.Len(); i++ { collection.SetMapIndex(value.Index(i), emptyValue) } return collection.Interface() } func mapSlice(arrValue reflect.Value, funcValue reflect.Value) reflect.Value { funcType := funcValue.Type() if funcType.NumIn() != 1 || funcType.NumOut() == 0 || funcType.NumOut() > 2 { panic("Map function with an array must have one parameter and must return one or two parameters") } arrElemType := arrValue.Type().Elem() // Checking whether element type is convertible to function's first argument's type. if !arrElemType.ConvertibleTo(funcType.In(0)) { panic("Map function's argument is not compatible with type of array.") } if funcType.NumOut() == 1 { // Get slice type corresponding to function's return value's type. resultSliceType := reflect.SliceOf(funcType.Out(0)) // MakeSlice takes a slice kind type, and makes a slice. resultSlice := reflect.MakeSlice(resultSliceType, 0, 0) for i := 0; i < arrValue.Len(); i++ { result := funcValue.Call([]reflect.Value{arrValue.Index(i)})[0] resultSlice = reflect.Append(resultSlice, result) } return resultSlice } if funcType.NumOut() == 2 { // value of the map will be the input type collectionType := reflect.MapOf(funcType.Out(0), funcType.Out(1)) // create a map from scratch collection := reflect.MakeMap(collectionType) for i := 0; i < arrValue.Len(); i++ { results := funcValue.Call([]reflect.Value{arrValue.Index(i)}) collection.SetMapIndex(results[0], results[1]) } return collection } return reflect.Value{} } func mapMap(arrValue reflect.Value, funcValue reflect.Value) reflect.Value { funcType := funcValue.Type() if funcType.NumIn() != 2 || funcType.NumOut() == 0 || funcType.NumOut() > 2 { panic("Map function with a map must have two parameters and must return one or two parameters") } // Only one returned parameter, should be a slice if funcType.NumOut() == 1 { // Get slice type corresponding to function's return value's type. resultSliceType := reflect.SliceOf(funcType.Out(0)) // MakeSlice takes a slice kind type, and makes a slice. resultSlice := reflect.MakeSlice(resultSliceType, 0, 0) for _, key := range arrValue.MapKeys() { results := funcValue.Call([]reflect.Value{key, arrValue.MapIndex(key)}) result := results[0] resultSlice = reflect.Append(resultSlice, result) } return resultSlice } // two parameters, should be a map if funcType.NumOut() == 2 { // value of the map will be the input type collectionType := reflect.MapOf(funcType.Out(0), funcType.Out(1)) // create a map from scratch collection := reflect.MakeMap(collectionType) for _, key := range arrValue.MapKeys() { results := funcValue.Call([]reflect.Value{key, arrValue.MapIndex(key)}) collection.SetMapIndex(results[0], results[1]) } return collection } return reflect.Value{} } // Map manipulates an iteratee and transforms it to another type. func Map(arr interface{}, mapFunc interface{}) interface{} { result := mapFn(arr, mapFunc, "Map") if result.IsValid() { return result.Interface() } return nil } func mapFn(arr interface{}, mapFunc interface{}, funcName string) reflect.Value { if !IsIteratee(arr) { panic("First parameter must be an iteratee") } if !IsFunction(mapFunc) { panic("Second argument must be function") } var ( funcValue = reflect.ValueOf(mapFunc) arrValue = reflect.ValueOf(arr) arrType = arrValue.Type() ) kind := arrType.Kind() if kind == reflect.Slice || kind == reflect.Array { return mapSlice(arrValue, funcValue) } else if kind == reflect.Map { return mapMap(arrValue, funcValue) } panic(fmt.Sprintf("Type %s is not supported by "+funcName, arrType.String())) } // FlatMap manipulates an iteratee and transforms it to a flattened collection of another type. func FlatMap(arr interface{}, mapFunc interface{}) interface{} { result := mapFn(arr, mapFunc, "FlatMap") if result.IsValid() { return flatten(result).Interface() } return nil } // Flatten flattens a two-dimensional array. func Flatten(out interface{}) interface{} { return flatten(reflect.ValueOf(out)).Interface() } func flatten(value reflect.Value) reflect.Value { sliceType := value.Type() if (value.Kind() != reflect.Slice && value.Kind() != reflect.Array) || (sliceType.Elem().Kind() != reflect.Slice && sliceType.Elem().Kind() != reflect.Array) { panic("Argument must be an array or slice of at least two dimensions") } resultSliceType := sliceType.Elem().Elem() resultSlice := reflect.MakeSlice(reflect.SliceOf(resultSliceType), 0, 0) length := value.Len() for i := 0; i < length; i++ { item := value.Index(i) resultSlice = reflect.AppendSlice(resultSlice, item) } return resultSlice } // FlattenDeep recursively flattens array. func FlattenDeep(out interface{}) interface{} { return flattenDeep(reflect.ValueOf(out)).Interface() } func flattenDeep(value reflect.Value) reflect.Value { sliceType := sliceElem(value.Type()) resultSlice := reflect.MakeSlice(reflect.SliceOf(sliceType), 0, 0) return flattenRecursive(value, resultSlice) } func flattenRecursive(value reflect.Value, result reflect.Value) reflect.Value { length := value.Len() for i := 0; i < length; i++ { item := value.Index(i) kind := item.Kind() if kind == reflect.Slice || kind == reflect.Array { result = flattenRecursive(item, result) } else { result = reflect.Append(result, item) } } return result } // Shuffle creates an array of shuffled values func Shuffle(in interface{}) interface{} { value := reflect.ValueOf(in) valueType := value.Type() kind := value.Kind() if kind == reflect.Array || kind == reflect.Slice { length := value.Len() resultSlice := makeSlice(value, length) for i, v := range rand.Perm(length) { resultSlice.Index(i).Set(value.Index(v)) } return resultSlice.Interface() } panic(fmt.Sprintf("Type %s is not supported by Shuffle", valueType.String())) } // Reverse transforms an array the first element will become the last, // the second element will become the second to last, etc. func Reverse(in interface{}) interface{} { value := reflect.ValueOf(in) valueType := value.Type() kind := value.Kind() if kind == reflect.String { return ReverseString(in.(string)) } if kind == reflect.Array || kind == reflect.Slice { length := value.Len() resultSlice := makeSlice(value, length) j := 0 for i := length - 1; i >= 0; i-- { resultSlice.Index(j).Set(value.Index(i)) j++ } return resultSlice.Interface() } panic(fmt.Sprintf("Type %s is not supported by Reverse", valueType.String())) } // Uniq creates an array with unique values. func Uniq(in interface{}) interface{} { value := reflect.ValueOf(in) valueType := value.Type() kind := value.Kind() if kind == reflect.Array || kind == reflect.Slice { length := value.Len() result := makeSlice(value, 0) seen := make(map[interface{}]bool, length) for i := 0; i < length; i++ { val := value.Index(i) v := val.Interface() if _, ok := seen[v]; ok { continue } seen[v] = true result = reflect.Append(result, val) } return result.Interface() } panic(fmt.Sprintf("Type %s is not supported by Uniq", valueType.String())) } // Uniq creates an array with unique values. func UniqBy(in interface{}, mapFunc interface{}) interface{} { if !IsFunction(mapFunc) { panic("Second argument must be function") } value := reflect.ValueOf(in) valueType := value.Type() kind := value.Kind() funcValue := reflect.ValueOf(mapFunc) if kind == reflect.Array || kind == reflect.Slice { length := value.Len() result := makeSlice(value, 0) seen := make(map[interface{}]bool, length) for i := 0; i < length; i++ { val := value.Index(i) v := funcValue.Call([]reflect.Value{val})[0].Interface() if _, ok := seen[v]; ok { continue } seen[v] = true result = reflect.Append(result, val) } return result.Interface() } panic(fmt.Sprintf("Type %s is not supported by Uniq", valueType.String())) } // ConvertSlice converts a slice type to another, // a perfect example would be to convert a slice of struct to a slice of interface. func ConvertSlice(in interface{}, out interface{}) { srcValue := reflect.ValueOf(in) dstValue := reflect.ValueOf(out) if dstValue.Kind() != reflect.Ptr { panic("Second argument must be a pointer") } dstValue = dstValue.Elem() if srcValue.Kind() != reflect.Slice && srcValue.Kind() != reflect.Array { panic("First argument must be an array or slice") } if dstValue.Kind() != reflect.Slice && dstValue.Kind() != reflect.Array { panic("Second argument must be an array or slice") } // returns value that points to dstValue direct := reflect.Indirect(dstValue) length := srcValue.Len() for i := 0; i < length; i++ { dstValue = reflect.Append(dstValue, srcValue.Index(i)) } direct.Set(dstValue) } // Drop creates an array/slice with `n` elements dropped from the beginning. func Drop(in interface{}, n int) interface{} { value := reflect.ValueOf(in) valueType := value.Type() kind := value.Kind() if kind == reflect.Array || kind == reflect.Slice { length := value.Len() resultSlice := makeSlice(value, length-n) j := 0 for i := n; i < length; i++ { resultSlice.Index(j).Set(value.Index(i)) j++ } return resultSlice.Interface() } panic(fmt.Sprintf("Type %s is not supported by Drop", valueType.String())) } // Prune returns a copy of "in" that only contains fields in "paths" // which are looked up using struct field name. // For lookup paths by field tag instead, use funk.PruneByTag() func Prune(in interface{}, paths []string) (interface{}, error) { return pruneByTag(in, paths, nil /*tag*/) } // pruneByTag returns a copy of "in" that only contains fields in "paths" // which are looked up using struct field Tag "tag". func PruneByTag(in interface{}, paths []string, tag string) (interface{}, error) { return pruneByTag(in, paths, &tag) } // pruneByTag returns a copy of "in" that only contains fields in "paths" // which are looked up using struct field Tag "tag". If tag is nil, // traverse paths using struct field name func pruneByTag(in interface{}, paths []string, tag *string) (interface{}, error) { inValue := reflect.ValueOf(in) ret := reflect.New(inValue.Type()).Elem() for _, path := range paths { parts := strings.Split(path, ".") if err := prune(inValue, ret, parts, tag); err != nil { return nil, err } } return ret.Interface(), nil } func prune(inValue reflect.Value, ret reflect.Value, parts []string, tag *string) error { if len(parts) == 0 { // we reached the location that ret needs to hold inValue // Note: The value at the end of the path is not copied, maybe we need to change. // ret and the original data holds the same reference to this value ret.Set(inValue) return nil } inKind := inValue.Kind() switch inKind { case reflect.Ptr: if inValue.IsNil() { // TODO validate return nil } if ret.IsNil() { // init ret and go to next level ret.Set(reflect.New(inValue.Type().Elem())) } return prune(inValue.Elem(), ret.Elem(), parts, tag) case reflect.Struct: part := parts[0] var fValue reflect.Value var fRet reflect.Value if tag == nil { // use field name fValue = inValue.FieldByName(part) if !fValue.IsValid() { return fmt.Errorf("field name %v is not found in struct %v", part, inValue.Type().String()) } fRet = ret.FieldByName(part) } else { // search tag that has key equal to part found := false for i := 0; i < inValue.NumField(); i++ { f := inValue.Type().Field(i) if key, ok := f.Tag.Lookup(*tag); ok { if key == part { fValue = inValue.Field(i) fRet = ret.Field(i) found = true break } } } if !found { return fmt.Errorf("struct tag %v is not found with key %v", *tag, part) } } // init Ret is zero and go down one more level if fRet.IsZero() { fRet.Set(reflect.New(fValue.Type()).Elem()) } return prune(fValue, fRet, parts[1:], tag) case reflect.Array, reflect.Slice: // set all its elements length := inValue.Len() // init ret if ret.IsZero() { if inKind == reflect.Slice { ret.Set(reflect.MakeSlice(inValue.Type(), length /*len*/, length /*cap*/)) } else { // array ret.Set(reflect.New(inValue.Type()).Elem()) } } for j := 0; j < length; j++ { if err := prune(inValue.Index(j), ret.Index(j), parts, tag); err != nil { return err } } default: return fmt.Errorf("path %v cannot be looked up on kind of %v", strings.Join(parts, "."), inValue.Kind()) } return nil } go-funk-0.9.3/transform_test.go000066400000000000000000000274171435240704200165060ustar00rootroot00000000000000package funk import ( "database/sql" "fmt" "reflect" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestMap(t *testing.T) { is := assert.New(t) r := Map([]int{1, 2, 3, 4}, func(x int) string { return "Hello" }) result, ok := r.([]string) is.True(ok) is.Equal(len(result), 4) r = Map([]int{1, 2, 3, 4}, func(x int) (int, int) { return x, x }) resultType := reflect.TypeOf(r) is.True(resultType.Kind() == reflect.Map) is.True(resultType.Key().Kind() == reflect.Int) is.True(resultType.Elem().Kind() == reflect.Int) mapping := map[int]string{ 1: "Florent", 2: "Gilles", } r = Map(mapping, func(k int, v string) int { return k }) is.True(reflect.TypeOf(r).Kind() == reflect.Slice) is.True(reflect.TypeOf(r).Elem().Kind() == reflect.Int) r = Map(mapping, func(k int, v string) (string, string) { return fmt.Sprintf("%d", k), v }) resultType = reflect.TypeOf(r) is.True(resultType.Kind() == reflect.Map) is.True(resultType.Key().Kind() == reflect.String) is.True(resultType.Elem().Kind() == reflect.String) } func TestFlatMap(t *testing.T) { is := assert.New(t) x := reflect.Value{}.IsValid() fmt.Println(x) r := FlatMap([][]int{{1}, {2}, {3}, {4}}, func(x []int) []int { return x }) result, ok := r.([]int) is.True(ok) is.ElementsMatch(result, []int{1, 2, 3, 4}) mapping := map[string][]int{ "a": {1}, "b": {2}, } r = FlatMap(mapping, func(k string, v []int) []int { return v }) result, ok = r.([]int) is.True(ok) is.ElementsMatch(result, []int{1, 2}) } func TestToMap(t *testing.T) { is := assert.New(t) f1 := Foo{ ID: 1, FirstName: "Dark", LastName: "Vador", Age: 30, Bar: &Bar{ Name: "Test", }, } f2 := Foo{ ID: 1, FirstName: "Light", LastName: "Vador", Age: 30, Bar: &Bar{ Name: "Test", }, } // []*Foo -> Map sliceResults := []*Foo{&f1, &f2} instanceMapByID := ToMap(sliceResults, "ID") is.True(reflect.TypeOf(instanceMapByID).Kind() == reflect.Map) mappingByID, ok := instanceMapByID.(map[int]*Foo) is.True(ok) is.True(len(mappingByID) == 1) for _, result := range sliceResults { item, ok := mappingByID[result.ID] is.True(ok) is.True(reflect.TypeOf(item).Kind() == reflect.Ptr) is.True(reflect.TypeOf(item).Elem().Kind() == reflect.Struct) is.Equal(item.ID, result.ID) } // Array -> Map arrayResults := [4]Foo{f1, f1, f2, f2} instanceMapByFirstName := ToMap(arrayResults, "FirstName") is.True(reflect.TypeOf(instanceMapByFirstName).Kind() == reflect.Map) mappingByFirstName, ok := instanceMapByFirstName.(map[string]Foo) is.True(ok) is.True(len(mappingByFirstName) == 2) for _, result := range arrayResults { item, ok := mappingByFirstName[result.FirstName] is.True(ok) is.True(reflect.TypeOf(item).Kind() == reflect.Struct) is.Equal(item.FirstName, result.FirstName) } } func TestToSet(t *testing.T) { is := assert.New(t) type Foo struct { ID int Name string } var ( f1 = Foo{ID: 1, Name: "hello"} f2 = Foo{ID: 1, Name: "hello"} ) // [2]Foo -> map[Foo]struct{} array := [2]Foo{f1, f2} resultOfArray := ToSet(array) is.True(reflect.TypeOf(resultOfArray).Kind() == reflect.Map) setFromArray, ok := resultOfArray.(map[Foo]struct{}) is.True(ok) is.True(len(setFromArray) == 1) for k, v := range setFromArray { is.True(reflect.TypeOf(v).Size() == 0) is.True(k == f1) } // []*Foo -> map[*Foo]struct{} slice := []*Foo{&f1, &f2, &f1, &f2} resultOfSlice := ToSet(slice) is.True(reflect.TypeOf(resultOfSlice).Kind() == reflect.Map) setFromSlice, ok := resultOfSlice.(map[*Foo]struct{}) is.True(ok) is.True(len(setFromSlice) == 2) for k, v := range setFromSlice { is.True(reflect.TypeOf(v).Size() == 0) is.True(k == &f1 || k == &f2) } } func TestChunk(t *testing.T) { is := assert.New(t) results := Chunk([]int{0, 1, 2, 3, 4}, 2).([][]int) is.Len(results, 3) is.Len(results[0], 2) is.Len(results[1], 2) is.Len(results[2], 1) is.Len(Chunk([]int{}, 2), 0) is.Len(Chunk([]int{1}, 2), 1) is.Len(Chunk([]int{1, 2, 3}, 0), 3) } func TestFlatten(t *testing.T) { is := assert.New(t) is.Equal(Flatten([][][]int{{{1, 2}}, {{3, 4}}}), [][]int{{1, 2}, {3, 4}}) } func TestFlattenDeep(t *testing.T) { is := assert.New(t) is.Equal(FlattenDeep([][][]int{{{1, 2}}, {{3, 4}}}), []int{1, 2, 3, 4}) } func TestShuffle(t *testing.T) { initial := []int{0, 1, 2, 3, 4} results := Shuffle(initial) is := assert.New(t) is.Len(results, 5) for _, entry := range initial { is.True(Contains(results, entry)) } } func TestReverse(t *testing.T) { results := Reverse([]int{0, 1, 2, 3, 4}) is := assert.New(t) is.Equal(Reverse("abcdefg"), "gfedcba") is.Len(results, 5) is.Equal(results, []int{4, 3, 2, 1, 0}) } func TestUniq(t *testing.T) { is := assert.New(t) results := Uniq([]int{0, 1, 1, 2, 3, 0, 0, 12}) is.Len(results, 5) is.Equal(results, []int{0, 1, 2, 3, 12}) results = Uniq([]string{"foo", "bar", "foo", "bar", "bar"}) is.Len(results, 2) is.Equal(results, []string{"foo", "bar"}) } func TestUniqBy(t *testing.T) { is := assert.New(t) results := UniqBy([]int{0, 1, 1, 2, 3, 0, 0, 12}, func(nbr int) int { return nbr % 3 }) fmt.Println(results) is.Len(results, 3) is.Equal(results, []int{0, 1, 2}) type foobar struct { foo string bar string } foobar1 := foobar{ foo: "foo", bar: "bar", } foobar2 := foobar{ foo: "foo", bar: "baz", } foobar3 := foobar{ foo: "foo", bar: "bar", } results = UniqBy([]foobar{foobar1, foobar2, foobar3}, func(f foobar) string { return f.foo + f.bar }) is.Len(results, 2) is.Equal(results, []foobar{foobar1, foobar2}) } func TestConvertSlice(t *testing.T) { instances := []*Foo{foo, foo2} var raw []Model ConvertSlice(instances, &raw) is := assert.New(t) is.Len(raw, len(instances)) } func TestDrop(t *testing.T) { results := Drop([]int{0, 1, 1, 2, 3, 0, 0, 12}, 3) is := assert.New(t) is.Len(results, 5) is.Equal([]int{2, 3, 0, 0, 12}, results) } func TestPrune(t *testing.T) { testCases := []struct { OriginalFoo *Foo Paths []string ExpectedFoo *Foo }{ { foo, []string{"FirstName"}, &Foo{ FirstName: foo.FirstName, }, }, { foo, []string{"FirstName", "ID"}, &Foo{ FirstName: foo.FirstName, ID: foo.ID, }, }, { foo, []string{"EmptyValue.Int64"}, &Foo{ EmptyValue: sql.NullInt64{ Int64: foo.EmptyValue.Int64, }, }, }, { foo, []string{"FirstName", "ID", "EmptyValue.Int64"}, &Foo{ FirstName: foo.FirstName, ID: foo.ID, EmptyValue: sql.NullInt64{ Int64: foo.EmptyValue.Int64, }, }, }, { foo, []string{"FirstName", "ID", "EmptyValue.Int64"}, &Foo{ FirstName: foo.FirstName, ID: foo.ID, EmptyValue: sql.NullInt64{ Int64: foo.EmptyValue.Int64, }, }, }, { foo, []string{"FirstName", "ID", "Bar"}, &Foo{ FirstName: foo.FirstName, ID: foo.ID, Bar: foo.Bar, }, }, { foo, []string{"Bar", "Bars"}, &Foo{ Bar: foo.Bar, Bars: foo.Bars, }, }, { foo, []string{"FirstName", "Bars.Name"}, &Foo{ FirstName: foo.FirstName, Bars: []*Bar{ {Name: bar.Name}, {Name: bar.Name}, }, }, }, { foo, []string{"Bars.Name", "Bars.Bars.Name"}, &Foo{ Bars: []*Bar{ {Name: bar.Name, Bars: []*Bar{{Name: "Level1-1"}, {Name: "Level1-2"}}}, {Name: bar.Name, Bars: []*Bar{{Name: "Level1-1"}, {Name: "Level1-2"}}}, }, }, }, { foo, []string{"BarInterface", "BarPointer"}, &Foo{ BarInterface: bar, BarPointer: &bar, }, }, } // pass to prune by pointer to struct for idx, tc := range testCases { t.Run(fmt.Sprintf("Prune pointer test case #%v", idx), func(t *testing.T) { is := assert.New(t) res, err := Prune(tc.OriginalFoo, tc.Paths) require.NoError(t, err) fooPrune := res.(*Foo) is.Equal(tc.ExpectedFoo, fooPrune) }) } // pass to prune by struct directly for idx, tc := range testCases { t.Run(fmt.Sprintf("Prune non pointer test case #%v", idx), func(t *testing.T) { is := assert.New(t) fooNonPtr := *tc.OriginalFoo res, err := Prune(fooNonPtr, tc.Paths) require.NoError(t, err) fooPrune := res.(Foo) is.Equal(*tc.ExpectedFoo, fooPrune) }) } // test PruneByTag TagTestCases := []struct { OriginalFoo *Foo Paths []string ExpectedFoo *Foo Tag string }{ { foo, []string{"tag 1", "tag 4.BarName"}, &Foo{ FirstName: foo.FirstName, Bar: &Bar{ Name: bar.Name, }, }, "tag_name", }, } for idx, tc := range TagTestCases { t.Run(fmt.Sprintf("PruneByTag test case #%v", idx), func(t *testing.T) { is := assert.New(t) fooNonPtr := *tc.OriginalFoo res, err := PruneByTag(fooNonPtr, tc.Paths, tc.Tag) require.NoError(t, err) fooPrune := res.(Foo) is.Equal(*tc.ExpectedFoo, fooPrune) }) } t.Run("Bar Slice", func(t *testing.T) { barSlice := []*Bar{bar, bar} barSlicePruned, err := pruneByTag(barSlice, []string{"Name"}, nil /*tag*/) require.NoError(t, err) assert.Equal(t, []*Bar{{Name: bar.Name}, {Name: bar.Name}}, barSlicePruned) }) t.Run("Bar Array", func(t *testing.T) { barArr := [2]*Bar{bar, bar} barArrPruned, err := pruneByTag(barArr, []string{"Name"}, nil /*tag*/) require.NoError(t, err) assert.Equal(t, [2]*Bar{{Name: bar.Name}, {Name: bar.Name}}, barArrPruned) }) // test values are copied and not referenced in return result // NOTE: pointers at the end of path are referenced. Maybe we need to make a copy t.Run("Copy Value Str", func(t *testing.T) { is := assert.New(t) fooTest := &Foo{ Bar: &Bar{ Name: "bar", }, } res, err := pruneByTag(fooTest, []string{"Bar.Name"}, nil) require.NoError(t, err) fooTestPruned := res.(*Foo) is.Equal(fooTest, fooTestPruned) // change pruned fooTestPruned.Bar.Name = "changed bar" // check original is unchanged is.Equal(fooTest.Bar.Name, "bar") }) // error cases errCases := []struct { InputFoo *Foo Paths []string TagName *string }{ { foo, []string{"NotExist"}, nil, }, { foo, []string{"FirstName.NotExist", "LastName"}, nil, }, { foo, []string{"LastName", "FirstName.NotExist"}, nil, }, { foo, []string{"LastName", "Bars.NotExist"}, nil, }, // tags { foo, []string{"tag 999"}, &[]string{"tag_name"}[0], }, { foo, []string{"tag 1.NotExist"}, &[]string{"tag_name"}[0], }, { foo, []string{"tag 4.NotExist"}, &[]string{"tag_name"}[0], }, { foo, []string{"FirstName"}, &[]string{"tag_name_not_exist"}[0], }, } for idx, errTC := range errCases { t.Run(fmt.Sprintf("error test case #%v", idx), func(t *testing.T) { _, err := pruneByTag(errTC.InputFoo, errTC.Paths, errTC.TagName) assert.Error(t, err) }) } } func ExamplePrune() { type ExampleFoo struct { ExampleFooPtr *ExampleFoo `json:"example_foo_ptr"` Name string `json:"name"` Number int `json:"number"` } exampleFoo := ExampleFoo{ ExampleFooPtr: &ExampleFoo{ Name: "ExampleFooPtr", Number: 2, }, Name: "ExampleFoo", Number: 1, } // prune using struct field name res, _ := Prune(exampleFoo, []string{"ExampleFooPtr.Name", "Number"}) prunedFoo := res.(ExampleFoo) fmt.Println(prunedFoo.ExampleFooPtr.Name) fmt.Println(prunedFoo.ExampleFooPtr.Number) fmt.Println(prunedFoo.Name) fmt.Println(prunedFoo.Number) // prune using struct json tag res2, _ := PruneByTag(exampleFoo, []string{"example_foo_ptr.name", "number"}, "json") prunedByTagFoo := res2.(ExampleFoo) fmt.Println(prunedByTagFoo.ExampleFooPtr.Name) fmt.Println(prunedByTagFoo.ExampleFooPtr.Number) fmt.Println(prunedByTagFoo.Name) fmt.Println(prunedByTagFoo.Number) // output: // ExampleFooPtr // 0 // // 1 // ExampleFooPtr // 0 // // 1 } go-funk-0.9.3/typesafe.go000066400000000000000000000657471435240704200152640ustar00rootroot00000000000000package funk import ( "math/rand" ) // InBools is an alias of ContainsBool, returns true if a bool is present in a iteratee. func InBools(s []bool, v bool) bool { return ContainsBool(s, v) } // InInts is an alias of ContainsInt, returns true if an int is present in a iteratee. func InInts(s []int, v int) bool { return ContainsInt(s, v) } // InInt32s is an alias of ContainsInt32, returns true if an int32 is present in a iteratee. func InInt32s(s []int32, v int32) bool { return ContainsInt32(s, v) } // InInt64s is an alias of ContainsInt64, returns true if an int64 is present in a iteratee. func InInt64s(s []int64, v int64) bool { return ContainsInt64(s, v) } // InUInts is an alias of ContainsUInt, returns true if an uint is present in a iteratee. func InUInts(s []uint, v uint) bool { return ContainsUInt(s, v) } // InUInt32s is an alias of ContainsUInt32, returns true if an uint32 is present in a iteratee. func InUInt32s(s []uint32, v uint32) bool { return ContainsUInt32(s, v) } // InUInt64s is an alias of ContainsUInt64, returns true if an uint64 is present in a iteratee. func InUInt64s(s []uint64, v uint64) bool { return ContainsUInt64(s, v) } // InStrings is an alias of ContainsString, returns true if a string is present in a iteratee. func InStrings(s []string, v string) bool { return ContainsString(s, v) } // InFloat32s is an alias of ContainsFloat32, returns true if a float32 is present in a iteratee. func InFloat32s(s []float32, v float32) bool { return ContainsFloat32(s, v) } // InFloat64s is an alias of ContainsFloat64, returns true if a float64 is present in a iteratee. func InFloat64s(s []float64, v float64) bool { return ContainsFloat64(s, v) } // FindFloat64 iterates over a collection of float64, returning an array of // all float64 elements predicate returns truthy for. func FindFloat64(s []float64, cb func(s float64) bool) (float64, bool) { for _, i := range s { result := cb(i) if result { return i, true } } return 0.0, false } // FindFloat32 iterates over a collection of float32, returning the first // float32 element predicate returns truthy for. func FindFloat32(s []float32, cb func(s float32) bool) (float32, bool) { for _, i := range s { result := cb(i) if result { return i, true } } return 0.0, false } // FindInt iterates over a collection of int, returning the first // int element predicate returns truthy for. func FindInt(s []int, cb func(s int) bool) (int, bool) { for _, i := range s { result := cb(i) if result { return i, true } } return 0, false } // FindInt32 iterates over a collection of int32, returning the first // int32 element predicate returns truthy for. func FindInt32(s []int32, cb func(s int32) bool) (int32, bool) { for _, i := range s { result := cb(i) if result { return i, true } } return 0, false } // FindInt64 iterates over a collection of int64, returning the first // int64 element predicate returns truthy for. func FindInt64(s []int64, cb func(s int64) bool) (int64, bool) { for _, i := range s { result := cb(i) if result { return i, true } } return 0, false } // FindString iterates over a collection of string, returning the first // string element predicate returns truthy for. func FindString(s []string, cb func(s string) bool) (string, bool) { for _, i := range s { result := cb(i) if result { return i, true } } return "", false } // FilterBool iterates over a collection of bool, returning an array of // all bool elements predicate returns truthy for. func FilterBool(s []bool, cb func(s bool) bool) []bool { results := []bool{} for _, i := range s { result := cb(i) if result { results = append(results, i) } } return results } // FilterFloat64 iterates over a collection of float64, returning an array of // all float64 elements predicate returns truthy for. func FilterFloat64(s []float64, cb func(s float64) bool) []float64 { results := []float64{} for _, i := range s { result := cb(i) if result { results = append(results, i) } } return results } // FilterFloat32 iterates over a collection of float32, returning an array of // all float32 elements predicate returns truthy for. func FilterFloat32(s []float32, cb func(s float32) bool) []float32 { results := []float32{} for _, i := range s { result := cb(i) if result { results = append(results, i) } } return results } // FilterInt iterates over a collection of int, returning an array of // all int elements predicate returns truthy for. func FilterInt(s []int, cb func(s int) bool) []int { results := []int{} for _, i := range s { result := cb(i) if result { results = append(results, i) } } return results } // FilterInt32 iterates over a collection of int32, returning an array of // all int32 elements predicate returns truthy for. func FilterInt32(s []int32, cb func(s int32) bool) []int32 { results := []int32{} for _, i := range s { result := cb(i) if result { results = append(results, i) } } return results } // FilterInt64 iterates over a collection of int64, returning an array of // all int64 elements predicate returns truthy for. func FilterInt64(s []int64, cb func(s int64) bool) []int64 { results := []int64{} for _, i := range s { result := cb(i) if result { results = append(results, i) } } return results } // FilterUInt iterates over a collection of uint, returning an array of // all uint elements predicate returns truthy for. func FilterUInt(s []uint, cb func(s uint) bool) []uint { results := []uint{} for _, i := range s { result := cb(i) if result { results = append(results, i) } } return results } // FilterUInt32 iterates over a collection of uint32, returning an array of // all uint32 elements predicate returns truthy for. func FilterUInt32(s []uint32, cb func(s uint32) bool) []uint32 { results := []uint32{} for _, i := range s { result := cb(i) if result { results = append(results, i) } } return results } // FilterUInt64 iterates over a collection of uint64, returning an array of // all uint64 elements predicate returns truthy for. func FilterUInt64(s []uint64, cb func(s uint64) bool) []uint64 { results := []uint64{} for _, i := range s { result := cb(i) if result { results = append(results, i) } } return results } // FilterString iterates over a collection of string, returning an array of // all string elements predicate returns truthy for. func FilterString(s []string, cb func(s string) bool) []string { results := []string{} for _, i := range s { result := cb(i) if result { results = append(results, i) } } return results } // ContainsBool returns true if a boolean is present in a iteratee. func ContainsBool(s []bool, v bool) bool { for _, vv := range s { if vv == v { return true } } return false } // ContainsInt returns true if an int is present in a iteratee. func ContainsInt(s []int, v int) bool { for _, vv := range s { if vv == v { return true } } return false } // ContainsInt32 returns true if an int32 is present in a iteratee. func ContainsInt32(s []int32, v int32) bool { for _, vv := range s { if vv == v { return true } } return false } // ContainsInt64 returns true if an int64 is present in a iteratee. func ContainsInt64(s []int64, v int64) bool { for _, vv := range s { if vv == v { return true } } return false } // ContainsUInt returns true if an uint is present in a iteratee. func ContainsUInt(s []uint, v uint) bool { for _, vv := range s { if vv == v { return true } } return false } // ContainsUInt32 returns true if an uint32 is present in a iteratee. func ContainsUInt32(s []uint32, v uint32) bool { for _, vv := range s { if vv == v { return true } } return false } // ContainsUInt64 returns true if an uint64 is present in a iteratee. func ContainsUInt64(s []uint64, v uint64) bool { for _, vv := range s { if vv == v { return true } } return false } // ContainsString returns true if a string is present in a iteratee. func ContainsString(s []string, v string) bool { for _, vv := range s { if vv == v { return true } } return false } // ContainsFloat32 returns true if a float32 is present in a iteratee. func ContainsFloat32(s []float32, v float32) bool { for _, vv := range s { if vv == v { return true } } return false } // ContainsFloat64 returns true if a float64 is present in a iteratee. func ContainsFloat64(s []float64, v float64) bool { for _, vv := range s { if vv == v { return true } } return false } // SumInt32 sums a int32 iteratee and returns the sum of all elements func SumInt32(s []int32) (sum int32) { for _, v := range s { sum += v } return } // SumInt64 sums a int64 iteratee and returns the sum of all elements func SumInt64(s []int64) (sum int64) { for _, v := range s { sum += v } return } // SumInt sums a int iteratee and returns the sum of all elements func SumInt(s []int) (sum int) { for _, v := range s { sum += v } return } // SumUInt32 sums a uint32 iteratee and returns the sum of all elements func SumUInt32(s []uint32) (sum uint32) { for _, v := range s { sum += v } return } // SumUInt64 sums a uint64 iteratee and returns the sum of all elements func SumUInt64(s []uint64) (sum uint64) { for _, v := range s { sum += v } return } // SumUInt sums a uint iteratee and returns the sum of all elements func SumUInt(s []uint) (sum uint) { for _, v := range s { sum += v } return } // SumFloat64 sums a float64 iteratee and returns the sum of all elements func SumFloat64(s []float64) (sum float64) { for _, v := range s { sum += v } return } // SumFloat32 sums a float32 iteratee and returns the sum of all elements func SumFloat32(s []float32) (sum float32) { for _, v := range s { sum += v } return } // ReverseBools reverses an array of bool func ReverseBools(s []bool) []bool { for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { s[i], s[j] = s[j], s[i] } return s } // ReverseStrings reverses an array of string func ReverseStrings(s []string) []string { for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { s[i], s[j] = s[j], s[i] } return s } // ReverseInt reverses an array of int func ReverseInt(s []int) []int { for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { s[i], s[j] = s[j], s[i] } return s } // ReverseInt32 reverses an array of int32 func ReverseInt32(s []int32) []int32 { for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { s[i], s[j] = s[j], s[i] } return s } // ReverseInt64 reverses an array of int64 func ReverseInt64(s []int64) []int64 { for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { s[i], s[j] = s[j], s[i] } return s } // ReverseUInt reverses an array of int func ReverseUInt(s []uint) []uint { for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { s[i], s[j] = s[j], s[i] } return s } // ReverseUInt32 reverses an array of uint32 func ReverseUInt32(s []uint32) []uint32 { for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { s[i], s[j] = s[j], s[i] } return s } // ReverseUInt64 reverses an array of uint64 func ReverseUInt64(s []uint64) []uint64 { for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { s[i], s[j] = s[j], s[i] } return s } // ReverseFloat64 reverses an array of float64 func ReverseFloat64(s []float64) []float64 { for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { s[i], s[j] = s[j], s[i] } return s } // ReverseFloat32 reverses an array of float32 func ReverseFloat32(s []float32) []float32 { for i, j := 0, len(s)-1; i < len(s)/2; i, j = i+1, j-1 { s[i], s[j] = s[j], s[i] } return s } // ReverseString reverses a string func ReverseString(s string) string { r := []rune(s) for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 { r[i], r[j] = r[j], r[i] } return string(r) } func indexOf(n int, f func(int) bool) int { for i := 0; i < n; i++ { if f(i) { return i } } return -1 } // IndexOfBool gets the index at which the first occurrence of a bool value is found in array or return -1 // if the value cannot be found func IndexOfBool(a []bool, x bool) int { return indexOf(len(a), func(i int) bool { return a[i] == x }) } // IndexOfInt gets the index at which the first occurrence of an int value is found in array or return -1 // if the value cannot be found func IndexOfInt(a []int, x int) int { return indexOf(len(a), func(i int) bool { return a[i] == x }) } // IndexOfInt32 gets the index at which the first occurrence of an int32 value is found in array or return -1 // if the value cannot be found func IndexOfInt32(a []int32, x int32) int { return indexOf(len(a), func(i int) bool { return a[i] == x }) } // IndexOfInt64 gets the index at which the first occurrence of an int64 value is found in array or return -1 // if the value cannot be found func IndexOfInt64(a []int64, x int64) int { return indexOf(len(a), func(i int) bool { return a[i] == x }) } // IndexOfUInt gets the index at which the first occurrence of an uint value is found in array or return -1 // if the value cannot be found func IndexOfUInt(a []uint, x uint) int { return indexOf(len(a), func(i int) bool { return a[i] == x }) } // IndexOfUInt32 gets the index at which the first occurrence of an uint32 value is found in array or return -1 // if the value cannot be found func IndexOfUInt32(a []uint32, x uint32) int { return indexOf(len(a), func(i int) bool { return a[i] == x }) } // IndexOfUInt64 gets the index at which the first occurrence of an uint64 value is found in array or return -1 // if the value cannot be found func IndexOfUInt64(a []uint64, x uint64) int { return indexOf(len(a), func(i int) bool { return a[i] == x }) } // IndexOfFloat64 gets the index at which the first occurrence of an float64 value is found in array or return -1 // if the value cannot be found func IndexOfFloat64(a []float64, x float64) int { return indexOf(len(a), func(i int) bool { return a[i] == x }) } // IndexOfString gets the index at which the first occurrence of a string value is found in array or return -1 // if the value cannot be found func IndexOfString(a []string, x string) int { return indexOf(len(a), func(i int) bool { return a[i] == x }) } func lastIndexOf(n int, f func(int) bool) int { for i := n - 1; i >= 0; i-- { if f(i) { return i } } return -1 } // LastIndexOfBool gets the index at which the first occurrence of a bool value is found in array or return -1 // if the value cannot be found func LastIndexOfBool(a []bool, x bool) int { return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) } // LastIndexOfInt gets the index at which the first occurrence of an int value is found in array or return -1 // if the value cannot be found func LastIndexOfInt(a []int, x int) int { return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) } // LastIndexOfInt32 gets the index at which the first occurrence of an int32 value is found in array or return -1 // if the value cannot be found func LastIndexOfInt32(a []int32, x int32) int { return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) } // LastIndexOfInt64 gets the index at which the first occurrence of an int64 value is found in array or return -1 // if the value cannot be found func LastIndexOfInt64(a []int64, x int64) int { return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) } // LastIndexOfUInt gets the index at which the first occurrence of an uint value is found in array or return -1 // if the value cannot be found func LastIndexOfUInt(a []uint, x uint) int { return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) } // LastIndexOfUInt32 gets the index at which the first occurrence of an uint32 value is found in array or return -1 // if the value cannot be found func LastIndexOfUInt32(a []uint32, x uint32) int { return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) } // LastIndexOfUInt64 gets the index at which the first occurrence of an uint64 value is found in array or return -1 // if the value cannot be found func LastIndexOfUInt64(a []uint64, x uint64) int { return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) } // LastIndexOfFloat64 gets the index at which the first occurrence of an float64 value is found in array or return -1 // if the value cannot be found func LastIndexOfFloat64(a []float64, x float64) int { return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) } // LastIndexOfFloat32 gets the index at which the first occurrence of an float32 value is found in array or return -1 // if the value cannot be found func LastIndexOfFloat32(a []float32, x float32) int { return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) } // LastIndexOfString gets the index at which the first occurrence of a string value is found in array or return -1 // if the value cannot be found func LastIndexOfString(a []string, x string) int { return lastIndexOf(len(a), func(i int) bool { return a[i] == x }) } // UniqBool creates an array of bool with unique values. func UniqBool(a []bool) []bool { results := []bool{} for _, value := range a { // If results is not empty, there is at most 1 value in it if len(results) == 0 || results[0] != value { results = append(results, value) } // At most 2 unique values if len(results) == 2 { break } } return results } // UniqInt32 creates an array of int32 with unique values. func UniqInt32(a []int32) []int32 { var ( length = len(a) seen = make(map[int32]struct{}, length) results = make([]int32, 0) ) for i := 0; i < length; i++ { v := a[i] if _, ok := seen[v]; ok { continue } seen[v] = struct{}{} results = append(results, v) } return results } // UniqInt64 creates an array of int64 with unique values. func UniqInt64(a []int64) []int64 { var ( length = len(a) seen = make(map[int64]struct{}, length) results = make([]int64, 0) ) for i := 0; i < length; i++ { v := a[i] if _, ok := seen[v]; ok { continue } seen[v] = struct{}{} results = append(results, v) } return results } // UniqInt creates an array of int with unique values. func UniqInt(a []int) []int { var ( length = len(a) seen = make(map[int]struct{}, length) results = make([]int, 0) ) for i := 0; i < length; i++ { v := a[i] if _, ok := seen[v]; ok { continue } seen[v] = struct{}{} results = append(results, v) } return results } // UniqUInt32 creates an array of uint32 with unique values. func UniqUInt32(a []uint32) []uint32 { var ( length = len(a) seen = make(map[uint32]struct{}, length) results = make([]uint32, 0) ) for i := 0; i < length; i++ { v := a[i] if _, ok := seen[v]; ok { continue } seen[v] = struct{}{} results = append(results, v) } return results } // UniqUInt64 creates an array of uint64 with unique values. func UniqUInt64(a []uint64) []uint64 { var ( length = len(a) seen = make(map[uint64]struct{}, length) results = make([]uint64, 0) ) for i := 0; i < length; i++ { v := a[i] if _, ok := seen[v]; ok { continue } seen[v] = struct{}{} results = append(results, v) } return results } // UniqUInt creates an array of uint with unique values. func UniqUInt(a []uint) []uint { var ( length = len(a) seen = make(map[uint]struct{}, length) results = make([]uint, 0) ) for i := 0; i < length; i++ { v := a[i] if _, ok := seen[v]; ok { continue } seen[v] = struct{}{} results = append(results, v) } return results } // UniqString creates an array of string with unique values. func UniqString(a []string) []string { var ( length = len(a) seen = make(map[string]struct{}, length) results = make([]string, 0) ) for i := 0; i < length; i++ { v := a[i] if _, ok := seen[v]; ok { continue } seen[v] = struct{}{} results = append(results, v) } return results } // UniqFloat64 creates an array of float64 with unique values. func UniqFloat64(a []float64) []float64 { var ( length = len(a) seen = make(map[float64]struct{}, length) results = make([]float64, 0) ) for i := 0; i < length; i++ { v := a[i] if _, ok := seen[v]; ok { continue } seen[v] = struct{}{} results = append(results, v) } return results } // UniqFloat32 creates an array of float32 with unique values. func UniqFloat32(a []float32) []float32 { var ( length = len(a) seen = make(map[float32]struct{}, length) results = make([]float32, 0) ) for i := 0; i < length; i++ { v := a[i] if _, ok := seen[v]; ok { continue } seen[v] = struct{}{} results = append(results, v) } return results } // ShuffleBool creates an array of bool shuffled values using Fisher–Yates algorithm func ShuffleBool(a []bool) []bool { for i := range a { j := rand.Intn(i + 1) a[i], a[j] = a[j], a[i] } return a } // ShuffleInt creates an array of int shuffled values using Fisher–Yates algorithm func ShuffleInt(a []int) []int { for i := range a { j := rand.Intn(i + 1) a[i], a[j] = a[j], a[i] } return a } // ShuffleInt32 creates an array of int32 shuffled values using Fisher–Yates algorithm func ShuffleInt32(a []int32) []int32 { for i := range a { j := rand.Intn(i + 1) a[i], a[j] = a[j], a[i] } return a } // ShuffleInt64 creates an array of int64 shuffled values using Fisher–Yates algorithm func ShuffleInt64(a []int64) []int64 { for i := range a { j := rand.Intn(i + 1) a[i], a[j] = a[j], a[i] } return a } // ShuffleUInt creates an array of int shuffled values using Fisher–Yates algorithm func ShuffleUInt(a []uint) []uint { for i := range a { j := rand.Intn(i + 1) a[i], a[j] = a[j], a[i] } return a } // ShuffleUInt32 creates an array of uint32 shuffled values using Fisher–Yates algorithm func ShuffleUInt32(a []uint32) []uint32 { for i := range a { j := rand.Intn(i + 1) a[i], a[j] = a[j], a[i] } return a } // ShuffleUInt64 creates an array of uint64 shuffled values using Fisher–Yates algorithm func ShuffleUInt64(a []uint64) []uint64 { for i := range a { j := rand.Intn(i + 1) a[i], a[j] = a[j], a[i] } return a } // ShuffleString creates an array of string shuffled values using Fisher–Yates algorithm func ShuffleString(a []string) []string { for i := range a { j := rand.Intn(i + 1) a[i], a[j] = a[j], a[i] } return a } // ShuffleFloat32 creates an array of float32 shuffled values using Fisher–Yates algorithm func ShuffleFloat32(a []float32) []float32 { for i := range a { j := rand.Intn(i + 1) a[i], a[j] = a[j], a[i] } return a } // ShuffleFloat64 creates an array of float64 shuffled values using Fisher–Yates algorithm func ShuffleFloat64(a []float64) []float64 { for i := range a { j := rand.Intn(i + 1) a[i], a[j] = a[j], a[i] } return a } // DropBool creates a slice with `n` bools dropped from the beginning. func DropBool(s []bool, n int) []bool { return s[n:] } // DropString creates a slice with `n` strings dropped from the beginning. func DropString(s []string, n int) []string { return s[n:] } // DropInt creates a slice with `n` ints dropped from the beginning. func DropInt(s []int, n int) []int { return s[n:] } // DropInt32 creates a slice with `n` int32s dropped from the beginning. func DropInt32(s []int32, n int) []int32 { return s[n:] } // DropInt64 creates a slice with `n` int64s dropped from the beginning. func DropInt64(s []int64, n int) []int64 { return s[n:] } // DropUInt creates a slice with `n` ints dropped from the beginning. func DropUInt(s []uint, n uint) []uint { return s[n:] } // DropUInt32 creates a slice with `n` int32s dropped from the beginning. func DropUInt32(s []uint32, n int) []uint32 { return s[n:] } // DropUInt64 creates a slice with `n` int64s dropped from the beginning. func DropUInt64(s []uint64, n int) []uint64 { return s[n:] } // DropFloat32 creates a slice with `n` float32s dropped from the beginning. func DropFloat32(s []float32, n int) []float32 { return s[n:] } // DropFloat64 creates a slice with `n` float64s dropped from the beginning. func DropFloat64(s []float64, n int) []float64 { return s[n:] } // ChunkStrings creates an array of strings split into groups with the length of size. // If array can't be split evenly, the final chunk will be // the remaining element. func ChunkStrings(arr []string, size int) [][]string { var results [][]string for i := 0; i < len(arr); i += size { end := i + size if end > len(arr) { end = len(arr) } results = append(results, arr[i:end]) } return results } // ChunkInts creates an array of ints split into groups with the length of size. // If array can't be split evenly, the final chunk will be // the remaining element. func ChunkInts(arr []int, size int) [][]int { var results [][]int for i := 0; i < len(arr); i += size { end := i + size if end > len(arr) { end = len(arr) } results = append(results, arr[i:end]) } return results } // ChunkInt32s creates an array of int32s split into groups with the length of size. // If array can't be split evenly, the final chunk will be // the remaining element. func ChunkInt32s(arr []int32, size int) [][]int32 { var results [][]int32 for i := 0; i < len(arr); i += size { end := i + size if end > len(arr) { end = len(arr) } results = append(results, arr[i:end]) } return results } // ChunkInt64s creates an array of int64s split into groups with the length of size. // If array can't be split evenly, the final chunk will be // the remaining element. func ChunkInt64s(arr []int64, size int) [][]int64 { var results [][]int64 for i := 0; i < len(arr); i += size { end := i + size if end > len(arr) { end = len(arr) } results = append(results, arr[i:end]) } return results } // ChunkUInts creates an array of uints split into groups with the length of size. // If array can't be split evenly, the final chunk will be // the remaining element. func ChunkUInts(arr []uint, size int) [][]uint { var results [][]uint for i := 0; i < len(arr); i += size { end := i + size if end > len(arr) { end = len(arr) } results = append(results, arr[i:end]) } return results } // ChunkUInt32s creates an array of uint32s split into groups with the length of size. // If array can't be split evenly, the final chunk will be // the remaining element. func ChunkUInt32s(arr []uint32, size int) [][]uint32 { var results [][]uint32 for i := 0; i < len(arr); i += size { end := i + size if end > len(arr) { end = len(arr) } results = append(results, arr[i:end]) } return results } // ChunkUInt64s creates an array of uint64s split into groups with the length of size. // If array can't be split evenly, the final chunk will be // the remaining element. func ChunkUInt64s(arr []uint64, size int) [][]uint64 { var results [][]uint64 for i := 0; i < len(arr); i += size { end := i + size if end > len(arr) { end = len(arr) } results = append(results, arr[i:end]) } return results } // ChunkFloat64s creates an array of float64s split into groups with the length of size. // If array can't be split evenly, the final chunk will be // the remaining element. func ChunkFloat64s(arr []float64, size int) [][]float64 { var results [][]float64 for i := 0; i < len(arr); i += size { end := i + size if end > len(arr) { end = len(arr) } results = append(results, arr[i:end]) } return results } go-funk-0.9.3/typesafe_test.go000066400000000000000000000201321435240704200162760ustar00rootroot00000000000000package funk import ( "testing" "github.com/stretchr/testify/assert" ) func TestContainsBool(t *testing.T) { is := assert.New(t) is.True(ContainsBool([]bool{true, false}, true)) is.False(ContainsBool([]bool{true}, false)) } func TestContainsInt(t *testing.T) { is := assert.New(t) is.True(ContainsInt([]int{1, 2, 3, 4}, 4)) is.False(ContainsInt([]int{1, 2, 3, 4}, 5)) is.True(ContainsInt32([]int32{1, 2, 3, 4}, 4)) is.False(ContainsInt32([]int32{1, 2, 3, 4}, 5)) is.True(ContainsInt64([]int64{1, 2, 3, 4}, 4)) is.False(ContainsInt64([]int64{1, 2, 3, 4}, 5)) is.True(ContainsUInt([]uint{1, 2, 3, 4}, 4)) is.False(ContainsUInt([]uint{1, 2, 3, 4}, 5)) is.True(ContainsUInt32([]uint32{1, 2, 3, 4}, 4)) is.False(ContainsUInt32([]uint32{1, 2, 3, 4}, 5)) is.True(ContainsUInt64([]uint64{1, 2, 3, 4}, 4)) is.False(ContainsUInt64([]uint64{1, 2, 3, 4}, 5)) } func TestContainsString(t *testing.T) { is := assert.New(t) is.True(ContainsString([]string{"flo", "gilles"}, "flo")) is.False(ContainsString([]string{"flo", "gilles"}, "alex")) } func TestFilterBool(t *testing.T) { is := assert.New(t) r := FilterBool([]bool{true, true, false, true}, func(x bool) bool { return x == true }) is.Equal(r, []bool{true, true, true}) } func TestFilterString(t *testing.T) { is := assert.New(t) r := FilterString([]string{"a", "b", "c", "d"}, func(x string) bool { return x >= "c" }) is.Equal(r, []string{"c", "d"}) } func TestFilterInt(t *testing.T) { is := assert.New(t) r := FilterInt([]int{1, 2, 3, 4}, func(x int) bool { return x%2 == 0 }) is.Equal(r, []int{2, 4}) } func TestFilterInt32(t *testing.T) { is := assert.New(t) r := FilterInt32([]int32{1, 2, 3, 4}, func(x int32) bool { return x%2 == 0 }) is.Equal(r, []int32{2, 4}) } func TestFilterInt64(t *testing.T) { is := assert.New(t) r := FilterInt64([]int64{1, 2, 3, 4}, func(x int64) bool { return x%2 == 0 }) is.Equal(r, []int64{2, 4}) } func TestFilterUInt(t *testing.T) { is := assert.New(t) r := FilterUInt([]uint{1, 2, 3, 4}, func(x uint) bool { return x%2 == 0 }) is.Equal(r, []uint{2, 4}) } func TestFilterUInt32(t *testing.T) { is := assert.New(t) r := FilterUInt32([]uint32{1, 2, 3, 4}, func(x uint32) bool { return x%2 == 0 }) is.Equal(r, []uint32{2, 4}) } func TestFilterUInt64(t *testing.T) { is := assert.New(t) r := FilterUInt64([]uint64{1, 2, 3, 4}, func(x uint64) bool { return x%2 == 0 }) is.Equal(r, []uint64{2, 4}) } func TestFilterFloat64(t *testing.T) { is := assert.New(t) r := FilterFloat64([]float64{1.0, 2.0, 3.0, 4.0}, func(x float64) bool { return int(x)%2 == 0 }) is.Equal(r, []float64{2.0, 4.0}) } func TestFilterFloat32(t *testing.T) { is := assert.New(t) r := FilterFloat32([]float32{1.0, 2.0, 3.0, 4.0}, func(x float32) bool { return int(x)%2 == 0 }) is.Equal(r, []float32{2.0, 4.0}) } func TestContainsFloat(t *testing.T) { is := assert.New(t) is.True(ContainsFloat64([]float64{0.1, 0.2}, 0.1)) is.False(ContainsFloat64([]float64{0.1, 0.2}, 0.3)) is.True(ContainsFloat32([]float32{0.1, 0.2}, 0.1)) is.False(ContainsFloat32([]float32{0.1, 0.2}, 0.3)) } func TestSumNumeral(t *testing.T) { is := assert.New(t) is.Equal(SumInt([]int{1, 2, 3}), 6) is.Equal(SumInt64([]int64{1, 2, 3}), int64(6)) is.Equal(SumUInt([]uint{1, 2, 3}), uint(6)) is.Equal(SumUInt64([]uint64{1, 2, 3}), uint64(6)) is.Equal(SumFloat32([]float32{0.1, 0.2, 0.1}), float32(0.4)) is.Equal(SumFloat64([]float64{0.1, 0.2, 0.1}), float64(0.4)) } func TestTypesafeReverse(t *testing.T) { is := assert.New(t) is.Equal(ReverseBools([]bool{true, false, false}), []bool{false, false, true}) is.Equal(ReverseString("abcdefg"), "gfedcba") is.Equal(ReverseInt([]int{1, 2, 3, 4}), []int{4, 3, 2, 1}) is.Equal(ReverseInt64([]int64{1, 2, 3, 4}), []int64{4, 3, 2, 1}) is.Equal(ReverseUInt([]uint{1, 2, 3, 4}), []uint{4, 3, 2, 1}) is.Equal(ReverseUInt64([]uint64{1, 2, 3, 4}), []uint64{4, 3, 2, 1}) is.Equal(ReverseStrings([]string{"flo", "gilles"}), []string{"gilles", "flo"}) is.Equal(ReverseFloat64([]float64{0.1, 0.2, 0.3}), []float64{0.3, 0.2, 0.1}) is.Equal(ReverseFloat32([]float32{0.1, 0.2, 0.3}), []float32{0.3, 0.2, 0.1}) } func TestTypesafeIndexOf(t *testing.T) { is := assert.New(t) is.Equal(IndexOfBool([]bool{true, false}, false), 1) is.Equal(IndexOfBool([]bool{true}, false), -1) is.Equal(IndexOfString([]string{"foo", "bar"}, "bar"), 1) is.Equal(IndexOfString([]string{"foo", "bar"}, "flo"), -1) is.Equal(IndexOfInt([]int{0, 1, 2}, 1), 1) is.Equal(IndexOfInt([]int{0, 1, 2}, 3), -1) is.Equal(IndexOfInt64([]int64{0, 1, 2}, 1), 1) is.Equal(IndexOfInt64([]int64{0, 1, 2}, 3), -1) is.Equal(IndexOfUInt64([]uint64{0, 1, 2}, 1), 1) is.Equal(IndexOfUInt64([]uint64{0, 1, 2}, 3), -1) is.Equal(IndexOfFloat64([]float64{0.1, 0.2, 0.3}, 0.2), 1) is.Equal(IndexOfFloat64([]float64{0.1, 0.2, 0.3}, 0.4), -1) } func TestTypesafeLastIndexOf(t *testing.T) { is := assert.New(t) is.Equal(LastIndexOfBool([]bool{true, true, false, true}, true), 3) is.Equal(LastIndexOfString([]string{"foo", "bar", "bar"}, "bar"), 2) is.Equal(LastIndexOfInt([]int{1, 2, 2, 3}, 2), 2) is.Equal(LastIndexOfInt64([]int64{1, 2, 2, 3}, 4), -1) is.Equal(LastIndexOfUInt([]uint{1, 2, 2, 3}, 2), 2) is.Equal(LastIndexOfUInt64([]uint64{1, 2, 2, 3}, 4), -1) } func TestTypesafeUniq(t *testing.T) { is := assert.New(t) is.Equal(UniqBool([]bool{true, false, false, true, false}), []bool{true, false}) is.Equal(UniqInt64([]int64{0, 1, 1, 2, 3, 0, 0, 12}), []int64{0, 1, 2, 3, 12}) is.Equal(UniqInt([]int{0, 1, 1, 2, 3, 0, 0, 12}), []int{0, 1, 2, 3, 12}) is.Equal(UniqUInt([]uint{0, 1, 1, 2, 3, 0, 0, 12}), []uint{0, 1, 2, 3, 12}) is.Equal(UniqUInt64([]uint64{0, 1, 1, 2, 3, 0, 0, 12}), []uint64{0, 1, 2, 3, 12}) is.Equal(UniqFloat64([]float64{0.0, 0.1, 0.1, 0.2, 0.3, 0.0, 0.0, 0.12}), []float64{0.0, 0.1, 0.2, 0.3, 0.12}) is.Equal(UniqString([]string{"foo", "bar", "foo", "bar"}), []string{"foo", "bar"}) } func TestTypesafeShuffle(t *testing.T) { is := assert.New(t) initial := []int{1, 2, 3, 5} results := ShuffleInt(initial) is.Len(results, 4) for _, entry := range initial { is.True(ContainsInt(results, entry)) } } func TestDropBool(t *testing.T) { results := DropBool([]bool{true, false, false, true, true}, 3) is := assert.New(t) is.Len(results, 2) is.Equal([]bool{true, true}, results) } func TestDropString(t *testing.T) { results := DropString([]string{"the", "quick", "brown", "fox", "jumps", "..."}, 3) is := assert.New(t) is.Len(results, 3) is.Equal([]string{"fox", "jumps", "..."}, results) } func TestDropInt(t *testing.T) { results := DropInt([]int{0, 0, 0, 0}, 3) is := assert.New(t) is.Len(results, 1) is.Equal([]int{0}, results) } func TestDropInt32(t *testing.T) { results := DropInt32([]int32{1, 2, 3, 4}, 3) is := assert.New(t) is.Len(results, 1) is.Equal([]int32{4}, results) } func TestDropInt64(t *testing.T) { results := DropInt64([]int64{1, 2, 3, 4}, 3) is := assert.New(t) is.Len(results, 1) is.Equal([]int64{4}, results) } func TestDropUInt(t *testing.T) { results := DropUInt([]uint{0, 0, 0, 0}, 3) is := assert.New(t) is.Len(results, 1) is.Equal([]uint{0}, results) } func TestDropUInt32(t *testing.T) { results := DropUInt32([]uint32{1, 2, 3, 4}, 3) is := assert.New(t) is.Len(results, 1) is.Equal([]uint32{4}, results) } func TestDropUInt64(t *testing.T) { results := DropUInt64([]uint64{1, 2, 3, 4}, 3) is := assert.New(t) is.Len(results, 1) is.Equal([]uint64{4}, results) } func TestDropFloat32(t *testing.T) { results := DropFloat32([]float32{1.1, 2.2, 3.3, 4.4}, 3) is := assert.New(t) is.Len(results, 1) is.Equal([]float32{4.4}, results) } func TestDropFloat64(t *testing.T) { results := DropFloat64([]float64{1.1, 2.2, 3.3, 4.4}, 3) is := assert.New(t) is.Len(results, 1) is.Equal([]float64{4.4}, results) } func TestChunkStrings(t *testing.T) { is := assert.New(t) results := ChunkStrings([]string{"foo", "bar", "foo", "bar", "bar"}, 2) is.Len(results, 3) is.Len(results[0], 2) is.Len(results[1], 2) is.Len(results[2], 1) is.Equal([]string{"foo", "bar"}, results[0]) is.Equal([]string{"foo", "bar"}, results[1]) is.Equal([]string{"bar"}, results[2]) } go-funk-0.9.3/union.go000066400000000000000000000026131435240704200145530ustar00rootroot00000000000000package funk import ( "reflect" ) // Union returns the union between two collections. func Union(collections ...interface{}) interface{} { // shortcut zero/single argument if len(collections) == 0 { return nil } else if len(collections) == 1 { return collections[0] } if !IsIteratee(collections[0]) { panic("Parameter must be a collection") } cType := reflect.TypeOf(collections[0]) zLen := 0 for i, x := range collections { xValue := reflect.ValueOf(x) xType := xValue.Type() if i > 0 && NotEqual(cType, xType) { panic("Parameters must have the same type") } zLen += xValue.Len() } if cType.Kind() == reflect.Map { zType := reflect.MapOf(cType.Key(), cType.Elem()) zMap := reflect.MakeMap(zType) for _, x := range collections { xIter := reflect.ValueOf(x).MapRange() for xIter.Next() { zMap.SetMapIndex(xIter.Key(), xIter.Value()) } } return zMap.Interface() } else { zType := reflect.SliceOf(cType.Elem()) zSlice := reflect.MakeSlice(zType, 0, 0) for _, x := range collections { xValue := reflect.ValueOf(x) zSlice = reflect.AppendSlice(zSlice, xValue) } return zSlice.Interface() } } // UnionStringMap returns the union between multiple string maps func UnionStringMap(x ...map[string]string) map[string]string { zMap := map[string]string{} for _, xMap := range x { for k, v := range xMap { zMap[k] = v } } return zMap } go-funk-0.9.3/union_test.go000066400000000000000000000013121435240704200156050ustar00rootroot00000000000000package funk import ( "testing" "github.com/stretchr/testify/assert" ) func TestUnion(t *testing.T) { is := assert.New(t) r := Union([]int{1, 2, 3, 4}, []int{2, 4, 6}) is.Equal(r, []int{1, 2, 3, 4, 2, 4, 6}) r = Union(map[int]int{1: 1, 2: 2}, map[int]int{1: 0, 3: 3}) is.Equal(r, map[int]int{1: 0, 2: 2, 3: 3}) } func TestUnionShortcut(t *testing.T) { is := assert.New(t) r := Union(nil) is.Nil(r) r = Union([]int{1, 2}) is.Equal(r, []int{1, 2}) } func TestUnionStringMap(t *testing.T) { is := assert.New(t) r := Union(map[string]string{"a": "a", "b": "b"}, map[string]string{"a": "z", "z": "a"}, map[string]string{"z": "z"}) is.Equal(r, map[string]string{"a": "z", "b": "b", "z": "z"}) } go-funk-0.9.3/utils.go000066400000000000000000000046521435240704200145700ustar00rootroot00000000000000package funk import ( "fmt" "reflect" ) func equal(expectedOrPredicate interface{}, optionalIsMap ...bool) func(keyValueIfMap, actualValue reflect.Value) bool { isMap := append(optionalIsMap, false)[0] if IsFunction(expectedOrPredicate) { inTypes := []reflect.Type{nil}; if isMap { inTypes = append(inTypes, nil) } if !IsPredicate(expectedOrPredicate, inTypes...) { panic(fmt.Sprintf("Predicate function must have %d parameter and must return boolean", len(inTypes))) } predicateValue := reflect.ValueOf(expectedOrPredicate) return func(keyValueIfMap, actualValue reflect.Value) bool { if isMap && !keyValueIfMap.Type().ConvertibleTo(predicateValue.Type().In(0)) { panic("Given key is not compatible with type of parameter for the predicate.") } if (isMap && !actualValue.Type().ConvertibleTo(predicateValue.Type().In(1))) || (!isMap && !actualValue.Type().ConvertibleTo(predicateValue.Type().In(0))) { panic("Given value is not compatible with type of parameter for the predicate.") } args := []reflect.Value{actualValue} if isMap { args = append([]reflect.Value{keyValueIfMap}, args...) } return predicateValue.Call(args)[0].Bool() } } expected := expectedOrPredicate return func(keyValueIfMap, actualValue reflect.Value) bool { if isMap { actualValue = keyValueIfMap } if expected == nil || actualValue.IsZero() { return actualValue.Interface() == expected } return reflect.DeepEqual(actualValue.Interface(), expected) } } func sliceElem(rtype reflect.Type) reflect.Type { for { if rtype.Kind() != reflect.Slice && rtype.Kind() != reflect.Array { return rtype } rtype = rtype.Elem() } } func redirectValue(value reflect.Value) reflect.Value { for { if !value.IsValid() || (value.Kind() != reflect.Ptr && value.Kind() != reflect.Interface) { return value } res := value.Elem() // Test for a circular type. if res.Kind() == reflect.Ptr && value.Kind() == reflect.Ptr && value.Pointer() == res.Pointer() { return value } if !res.IsValid() && value.Kind() == reflect.Ptr { return reflect.Zero(value.Type().Elem()) } value = res } } func makeSlice(value reflect.Value, values ...int) reflect.Value { sliceType := sliceElem(value.Type()) size := value.Len() cap := size if len(values) > 0 { size = values[0] } if len(values) > 1 { cap = values[1] } return reflect.MakeSlice(reflect.SliceOf(sliceType), size, cap) } go-funk-0.9.3/utils_test.go000066400000000000000000000003401435240704200156150ustar00rootroot00000000000000package funk import ( "reflect" "testing" "github.com/stretchr/testify/assert" ) func TestRedirectValue(t *testing.T) { is := assert.New(t) val := 1 is.Equal(redirectValue(reflect.ValueOf(&val)).Interface(), 1) } go-funk-0.9.3/without.go000066400000000000000000000007301435240704200151240ustar00rootroot00000000000000package funk import "reflect" // Without creates an array excluding all given values. func Without(in interface{}, values ...interface{}) interface{} { if !IsCollection(in) { panic("First parameter must be a collection") } inValue := reflect.ValueOf(in) for _, value := range values { if NotEqual(inValue.Type().Elem(), reflect.TypeOf(value)) { panic("Values must have the same type") } } return LeftJoin(inValue, reflect.ValueOf(values)).Interface() } go-funk-0.9.3/without_test.go000066400000000000000000000011321435240704200161600ustar00rootroot00000000000000package funk import ( "fmt" "testing" "github.com/stretchr/testify/assert" ) func TestWithout(t *testing.T) { testCases := []struct { Arr interface{} Values []interface{} Expect interface{} }{ {[]string{"foo", "bar"}, []interface{}{"bar"}, []string{"foo"}}, {[]int{0, 1, 2, 3, 4}, []interface{}{3, 4}, []int{0, 1, 2}}, {[]*Foo{f, b}, []interface{}{b, c}, []*Foo{f}}, } for idx, tt := range testCases { t.Run(fmt.Sprintf("test case #%d", idx+1), func(t *testing.T) { is := assert.New(t) actual := Without(tt.Arr, tt.Values...) is.Equal(tt.Expect, actual) }) } } go-funk-0.9.3/zip.go000066400000000000000000000017251435240704200142300ustar00rootroot00000000000000package funk import ( "reflect" ) // Tuple is the return type of Zip type Tuple struct { Element1 interface{} Element2 interface{} } // Zip returns a list of tuples, where the i-th tuple contains the i-th element // from each of the input iterables. The returned list is truncated in length // to the length of the shortest input iterable. func Zip(slice1 interface{}, slice2 interface{}) []Tuple { if !IsCollection(slice1) || !IsCollection(slice2) { panic("First parameter must be a collection") } var ( minLength int inValue1 = reflect.ValueOf(slice1) inValue2 = reflect.ValueOf(slice2) result = []Tuple{} length1 = inValue1.Len() length2 = inValue2.Len() ) if length1 <= length2 { minLength = length1 } else { minLength = length2 } for i := 0; i < minLength; i++ { newTuple := Tuple{ Element1: inValue1.Index(i).Interface(), Element2: inValue2.Index(i).Interface(), } result = append(result, newTuple) } return result } go-funk-0.9.3/zip_test.go000066400000000000000000000043241435240704200152650ustar00rootroot00000000000000package funk import ( "testing" "github.com/stretchr/testify/assert" ) func TestZipEmptyResult(t *testing.T) { map1 := map[string]int{"a": 1, "b": 2} array1 := []int{21, 22, 23} emptySlice := []int{} t.Run("NonSliceOrArray", func(t *testing.T) { assert.Panics(t, func() { Zip(map1, array1) }, "It should panic") }) t.Run("ZerosSized", func(t *testing.T) { expected := []Tuple{} result := Zip(emptySlice, array1) assert.Equal(t, result, expected) }) } func zipIntsAndAssert(t *testing.T, data1, data2 interface{}) { t.Run("FirstOneShorter", func(t *testing.T) { expected := []Tuple{ {Element1: 11, Element2: 21}, {Element1: 12, Element2: 22}, {Element1: 13, Element2: 23}, } result := Zip(data1, data2) assert.Equal(t, result, expected) }) t.Run("SecondOneShorter", func(t *testing.T) { expected := []Tuple{ {Element1: 21, Element2: 11}, {Element1: 22, Element2: 12}, {Element1: 23, Element2: 13}, } result := Zip(data2, data1) assert.Equal(t, result, expected) }) } func TestZipSlices(t *testing.T) { slice1 := []int{11, 12, 13} slice2 := []int{21, 22, 23, 24, 25} zipIntsAndAssert(t, slice1, slice2) } func TestZipArrays(t *testing.T) { array1 := [...]int{11, 12, 13} array2 := [...]int{21, 22, 23, 24, 25} zipIntsAndAssert(t, array1, array2) } func TestZipStructs(t *testing.T) { type struct1 struct { Member1 uint16 Member2 string } type struct2 struct { Member3 bool } type struct3 struct { Member4 int Member5 struct2 } slice1 := []struct1{ { Member1: 11, Member2: "a", }, { Member1: 12, Member2: "b", }, { Member1: 13, Member2: "c", }, } slice2 := []struct3{ { Member4: 21, Member5: struct2{ Member3: false, }, }, { Member4: 22, Member5: struct2{ Member3: true, }, }, } expected := []Tuple{ { Element1: struct1{ Member1: 11, Member2: "a", }, Element2: struct3{ Member4: 21, Member5: struct2{ Member3: false, }, }, }, { Element1: struct1{ Member1: 12, Member2: "b", }, Element2: struct3{ Member4: 22, Member5: struct2{ Member3: true, }, }, }, } result := Zip(slice1, slice2) assert.Equal(t, expected, result) }