├── .gitconfig ├── .githooks ├── commit-msg ├── install.py ├── post-commit └── pre-push ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_issue.md ├── pull_request_template.md └── workflows │ └── main.yml ├── .gitignore ├── CHANGELOG.rst ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── functions ├── once.go ├── once_test.go ├── run_after.go ├── run_after_test.go ├── wrap_func.go └── wrap_func_test.go ├── generals ├── duplicate.go ├── duplicate_test.go ├── same.go └── same_test.go ├── go.mod ├── go.sum ├── images ├── golang.png └── table-of-contents.png ├── internal └── utilities.go ├── maths ├── add.go ├── add_test.go ├── ceil.go ├── ceil_test.go ├── divide.go ├── divide_test.go ├── floor.go ├── floor_test.go ├── multiply.go ├── multiply_test.go ├── power.go ├── power_test.go ├── round.go ├── round_test.go ├── subtract.go └── subtract_test.go ├── numbers ├── clamp.go ├── clamp_test.go ├── in_range.go ├── in_range_test.go ├── random.go └── random_test.go ├── slices ├── chunk.go ├── chunk_test.go ├── compact.go ├── compact_test.go ├── concat.go ├── concat_test.go ├── difference.go ├── difference_by.go ├── difference_by_test.go ├── difference_test.go ├── drop.go ├── drop_by.go ├── drop_by_test.go ├── drop_right.go ├── drop_right_test.go ├── drop_test.go ├── fill.go ├── fill_test.go ├── find_index.go ├── find_index_by.go ├── find_index_by_test.go ├── find_index_test.go ├── first.go ├── flatten.go ├── flatten_deep.go ├── flatten_deep_test.go ├── flatten_depth.go ├── flatten_depth_test.go ├── flatten_test.go ├── from_pairs.go ├── from_pairs_test.go ├── head.go ├── head_test.go ├── index_of.go ├── index_of_test.go ├── initial.go ├── initial_test.go ├── intersection.go ├── intersection_by.go ├── intersection_by_test.go ├── intersection_test.go ├── join.go ├── join_test.go ├── last_index_of.go ├── last_index_of_test.go ├── latest_last.go ├── latest_last_test.go ├── max.go ├── max_by.go ├── max_by_test.go ├── max_test.go ├── mean.go ├── mean_by.go ├── mean_by_test.go ├── mean_test.go ├── min.go ├── min_by.go ├── min_by_test.go ├── min_test.go ├── n_th.go ├── n_th_test.go ├── pull.go ├── pull_at.go ├── pull_at_test.go ├── pull_test.go ├── remove_by.go ├── remove_by_test.go ├── reverse.go ├── reverse_test.go ├── slice.go ├── slice_test.go ├── sorted_index.go ├── sorted_index_by.go ├── sorted_index_by_test.go ├── sorted_index_of.go ├── sorted_index_of_test.go ├── sorted_index_test.go ├── sorted_last_index.go ├── sorted_last_index_by.go ├── sorted_last_index_by_test.go ├── sorted_last_index_of.go ├── sorted_last_index_of_test.go ├── sorted_last_index_test.go ├── sorted_unique.go ├── sorted_unique_by.go ├── sorted_unique_by_test.go ├── sorted_unique_test.go ├── sum.go ├── sum_by.go ├── sum_by_test.go ├── sum_test.go ├── tail.go ├── tail_test.go ├── take.go ├── take_right.go ├── take_right_test.go ├── take_right_while.go ├── take_right_while_test.go ├── take_test.go ├── take_while.go ├── take_while_test.go ├── union.go ├── union_by.go ├── union_by_test.go ├── union_test.go ├── unique.go ├── unique_by.go ├── unique_by_test.go ├── unique_test.go ├── unzip.go ├── unzip_test.go ├── without.go ├── xor.go ├── xor_by.go ├── xor_by_test.go ├── xor_test.go ├── zip.go ├── zip_by.go ├── zip_by_test.go ├── zip_map.go ├── zip_map_deep.go ├── zip_map_deep_test.go ├── zip_map_test.go └── zip_test.go └── strings ├── camel_case.go ├── camel_case_test.go ├── ends_with.go ├── ends_with_test.go ├── kebab_case.go ├── kebab_case_test.go ├── lower_case.go ├── lower_case_test.go ├── lower_first.go ├── lower_first_test.go ├── pad.go ├── pad_end.go ├── pad_end_test.go ├── pad_start.go ├── pad_start_test.go ├── pad_test.go ├── pascal_case.go ├── pascal_case_test.go ├── repeat.go ├── repeat_test.go ├── snake_case.go ├── snake_case_test.go ├── start_case.go ├── start_case_test.go ├── starts_with.go ├── starts_with_test.go ├── truncate.go ├── truncate_test.go ├── upper_first.go ├── upper_first_test.go ├── utils.go ├── words.go └── words_test.go /.gitconfig: -------------------------------------------------------------------------------- 1 | [core] 2 | autocrlf = input # convert all EOFs with lf when added to repo -------------------------------------------------------------------------------- /.githooks/install.py: -------------------------------------------------------------------------------- 1 | #!env/bin/python 2 | # Install hooks 3 | 4 | import os 5 | import sys 6 | 7 | text = "\thooksPath = .githooks" 8 | husky = "hooksPath = .husky" 9 | 10 | if not os.path.exists(".git"): 11 | print("no .git folder found") 12 | sys.exit(0) 13 | 14 | f = open(".git/config", "r") 15 | lines = f.readlines() 16 | lines_string = "" 17 | for line in lines: 18 | # adding config in core 19 | if line.strip() == "[core]": 20 | lines_string = line + text + "\n" 21 | continue 22 | # removing previous using husky tool 23 | if line.strip() != husky: 24 | lines_string += line 25 | # if configuration happened before, quit 26 | if line.strip().find(text.strip()) != -1: 27 | print("configurations happened before") 28 | sys.exit(0) 29 | f.close() 30 | 31 | f = open(".git/config", "w") 32 | f.write(lines_string + "\n") 33 | f.close() 34 | 35 | print("git hooks activated") -------------------------------------------------------------------------------- /.githooks/post-commit: -------------------------------------------------------------------------------- 1 | #!env/bin/python 2 | # Save CHANGELOG 3 | 4 | import os 5 | 6 | if os.path.isfile('.commit'): 7 | 8 | os.system('rm .commit') 9 | 10 | print("\nRecommit to add CHANGELOG.rst changes to the same commit:\n\n") 11 | 12 | os.system("git add CHANGELOG.rst") 13 | os.system("git commit --amend --no-edit") 14 | 15 | 16 | exit(0) -------------------------------------------------------------------------------- /.githooks/pre-push: -------------------------------------------------------------------------------- 1 | #!env/bin/python 2 | # Run test cases before push 3 | 4 | import os, sys 5 | 6 | packages = ["generals", "maths", "numbers", "slices", "strings", "functions"] 7 | 8 | for i in range(len(packages)): 9 | packages[i] = "./" + packages[i] 10 | 11 | f = open("go.mod") 12 | mod = f.readline() 13 | f.close() 14 | if mod == "": 15 | print("go.mod file is unreadable, please restore the changes to the file. `git restore go.mod`") 16 | sys.exit(1) 17 | mod = mod.replace("module ", "").strip() 18 | 19 | try: 20 | for package in packages: 21 | if os.path.isdir(package): 22 | files = os.listdir(package) 23 | hast_test_files = False 24 | for file in files: 25 | if "_test.go" in file: 26 | hast_test_files = True 27 | if hast_test_files: 28 | print(f"Testing {mod}/{package.replace('./', '')} package:\n") 29 | if os.system(f"go clean -testcache && go test -v {mod}/{package.replace('./', '')}") != 0: 30 | print(f"❌ {package.replace('./', '')} package test cases didn't succeed.") 31 | sys.exit(1) 32 | except: 33 | print('❌ All test cases didn\'t succeed.') 34 | sys.exit(1) 35 | 36 | print('\n✅ All test cases succeed.') 37 | 38 | print("""\n✨ Thanks for your Contribution ✨\n""") 39 | 40 | sys.exit(0) -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: When I did this, that happened. 4 | title: "[this] causes the bug" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Run the function like... 16 | 2. Function panics because... 17 | 3. ... 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Screenshots** 23 | If applicable, add screenshots to help explain your problem. 24 | 25 | **Environment:** 26 | - Go version: 1.18 27 | - Operation System: Ubuntu 20 28 | - ... 29 | 30 | **Additional context** 31 | Add any other context about the problem here. 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Issue 3 | about: I want to add [this] new feature. 4 | title: "[this], does that" 5 | labels: feature 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Feature Description** 11 | A clear and concise description of input and output of your new function or describe your new feature. 12 | 13 | [this] function is based on lodash documentation: [link to the feature documentation on lodash documentation] 14 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | ### Why: 8 | 9 | Closes # 10 | 11 | 12 | Added new feature, function, package or ... 13 | 14 | 17 | 18 | ### What's being changed: 19 | 20 | 22 | 23 | 31 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: GoTests-Main 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v2 18 | with: 19 | go-version: 1.17 20 | 21 | - name: Test 22 | run: go test -v ./... -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # env 2 | 3 | .env 4 | env.yaml 5 | env 6 | .commit 7 | main_repo 8 | 9 | # go 10 | 11 | vendor 12 | main.go 13 | 14 | # node 15 | 16 | node_modules 17 | 18 | # vscode 19 | 20 | .vscode 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 GoLodash Founders and other contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # What Is Godash? 2 | 3 |

4 | 5 |

6 | 7 | Godash is inspired by utility library [lodash](https://github.com/lodash/lodash). 8 | 9 | This library has many functions which make your life easier as a developer and 10 | solved different usual problems that every programmers face with them in their journey. 11 | 12 | ## How to install 13 | 14 | Simply write: 15 | ```bash 16 | go get github.com/golodash/godash 17 | ``` 18 | 19 | Note: version 1.X.X is compatible with go version 1.17 and higher. 20 | 21 | ## Documentation 22 | 23 | Whole documentation can be accessed in [godash.maktoobgar.ir](https://godash.maktoobgar.ir) web address. 24 | -------------------------------------------------------------------------------- /functions/once.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import "sync" 4 | 5 | // Creates a function that is restricted to invoking func once. Repeat calls 6 | // to the function return the value of the first invocation. 7 | // 8 | // Complexity: O(1) 9 | func Once(function func() []interface{}) func() []interface{} { 10 | lock := sync.Mutex{} 11 | done := false 12 | cache := []interface{}{} 13 | return func() []interface{} { 14 | lock.Lock() 15 | if done { 16 | lock.Unlock() 17 | } else { 18 | done = true 19 | cache = function() 20 | lock.Unlock() 21 | } 22 | return cache 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /functions/once_test.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestOnce(t *testing.T) { 9 | fmt.Println("I don't think any test cases can be written for Once") 10 | } 11 | 12 | func BenchmarkOnce(b *testing.B) { 13 | fmt.Println("I don't think any benchmarks can be written for Once") 14 | } 15 | -------------------------------------------------------------------------------- /functions/run_after.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "errors" 5 | "sync" 6 | "time" 7 | 8 | "github.com/golodash/godash/internal" 9 | ) 10 | 11 | type runCancel interface { 12 | Run() []interface{} 13 | Cancel() (bool, error) 14 | } 15 | 16 | type runCancelStruct struct { 17 | function func() []interface{} 18 | wait time.Duration 19 | parallel bool 20 | lock *sync.Mutex 21 | canceled bool 22 | inProgress bool 23 | done bool 24 | } 25 | 26 | // Returns an interface with two methods. one called Run which executes the 27 | // function and other one called Cancel which cancels executing of current 28 | // executed Run function. 29 | // 30 | // You can use WrapFunc to generate `function`. 31 | // 32 | // Complexity: O(1) 33 | func RunAfter(function func() []interface{}, wait time.Duration, parallel bool) runCancel { 34 | if ok := internal.IsFunc(function); !ok { 35 | panic("`function` input is not a function") 36 | } 37 | 38 | r := runCancelStruct{ 39 | function: function, 40 | wait: wait, 41 | parallel: parallel, 42 | lock: &sync.Mutex{}, 43 | canceled: false, 44 | inProgress: false, 45 | done: false, 46 | } 47 | 48 | return &r 49 | } 50 | 51 | func (r *runCancelStruct) Run() []interface{} { 52 | r.lock.Lock() 53 | r.reset() 54 | r.lock.Unlock() 55 | if r.parallel { 56 | ch := make(chan bool) 57 | go func() { 58 | value, ok := <-ch 59 | if ok && value { 60 | r.function() 61 | r.lock.Lock() 62 | defer r.lock.Unlock() 63 | r.done = true 64 | } else { 65 | return 66 | } 67 | }() 68 | go func() { 69 | time.Sleep(r.wait) 70 | r.lock.Lock() 71 | defer r.lock.Unlock() 72 | if !r.canceled { 73 | r.inProgress = true 74 | ch <- true 75 | } else { 76 | ch <- false 77 | } 78 | }() 79 | } else { 80 | time.Sleep(r.wait) 81 | return r.function() 82 | } 83 | 84 | return nil 85 | } 86 | 87 | func (r *runCancelStruct) Cancel() (bool, error) { 88 | if r.parallel { 89 | r.lock.Lock() 90 | defer r.lock.Unlock() 91 | if r.inProgress && !r.done { 92 | return false, errors.New("function in progress") 93 | } else if r.done { 94 | return false, errors.New("function done") 95 | } 96 | 97 | r.canceled = true 98 | return true, nil 99 | } else { 100 | return false, errors.New("parallel is off") 101 | } 102 | } 103 | 104 | func (r *runCancelStruct) reset() { 105 | r.canceled = false 106 | r.inProgress = false 107 | r.done = false 108 | } 109 | -------------------------------------------------------------------------------- /functions/run_after_test.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestRunAfter(t *testing.T) { 9 | fmt.Println("I don't think any test cases can be written for RunAfter") 10 | } 11 | 12 | func BenchmarkRunAfter(b *testing.B) { 13 | fmt.Println("I don't think any benchmarks can be written for RunAfter") 14 | } 15 | -------------------------------------------------------------------------------- /functions/wrap_func.go: -------------------------------------------------------------------------------- 1 | package functions 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Returns a function that invokes `function`, with passed `inputs` arguments. 10 | // 11 | // Complexity: O(n) 12 | // 13 | // n = length of `inputs` 14 | func WrapFunc(function interface{}, inputs ...interface{}) func() []interface{} { 15 | if ok := internal.IsFunc(function); !ok { 16 | panic("`function` input is not a function") 17 | } 18 | 19 | functionType := reflect.TypeOf(function) 20 | funcInLen := functionType.NumIn() 21 | inputsLen := len(inputs) 22 | defaultPanic := func() { panic("number of `function` inputs don't match with passed inputs") } 23 | if inputsLen != funcInLen { 24 | if funcInLen > inputsLen { 25 | if !functionType.IsVariadic() { 26 | defaultPanic() 27 | } else if funcInLen-1 != inputsLen { 28 | defaultPanic() 29 | } 30 | } else { 31 | if funcInLen != 0 { 32 | if !functionType.IsVariadic() { 33 | defaultPanic() 34 | } 35 | } else { 36 | defaultPanic() 37 | } 38 | } 39 | } 40 | 41 | funcInputs := []reflect.Value{} 42 | for i := 0; i < inputsLen; i++ { 43 | funcInputs = append(funcInputs, reflect.ValueOf(inputs[i])) 44 | } 45 | 46 | functionValue := reflect.ValueOf(function) 47 | return func() []interface{} { 48 | outputs := functionValue.Call(funcInputs) 49 | actualOutputs := []interface{}{} 50 | for i := 0; i < len(outputs); i++ { 51 | actualOutputs = append(actualOutputs, outputs[i].Interface()) 52 | } 53 | 54 | return actualOutputs 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /generals/duplicate.go: -------------------------------------------------------------------------------- 1 | package generals 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | "github.com/jinzhu/copier" 8 | ) 9 | 10 | // Takes almost any variable as input and returns a duplicate of the same 11 | // passed variable in return. 12 | // 13 | // Worst-Case Complexity: O(n) 14 | // 15 | // Best-Case Complexity: O(1) 16 | // 17 | // n = length of `input` 18 | func Duplicate(input interface{}) interface{} { 19 | inputType := reflect.TypeOf(input) 20 | inputValue := reflect.ValueOf(input) 21 | var secondInput reflect.Value = reflect.New(inputType) 22 | switch inputType.Kind() { 23 | case reflect.Slice: 24 | secondInput = reflect.MakeSlice(inputType, inputValue.Len(), inputValue.Len()) 25 | case reflect.Map: 26 | secondInput = reflect.MakeMapWithSize(inputType, inputValue.Len()) 27 | case reflect.Array: 28 | secondInput = reflect.New(reflect.ArrayOf(inputValue.Len(), inputType.Elem())).Elem() 29 | } 30 | if internal.IsPrimitive(inputType.Kind()) { 31 | second := input 32 | return second 33 | } 34 | 35 | second := secondInput.Interface() 36 | copier.Copy(&second, input) 37 | return second 38 | } 39 | -------------------------------------------------------------------------------- /generals/duplicate_test.go: -------------------------------------------------------------------------------- 1 | package generals 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/internal" 9 | ) 10 | 11 | type TDuplicate struct { 12 | name string 13 | value interface{} 14 | } 15 | 16 | var tDuplicateBenchs = []TDuplicate{ 17 | { 18 | name: "10", 19 | value: []int{}, 20 | }, 21 | { 22 | name: "100", 23 | value: []int{}, 24 | }, 25 | { 26 | name: "1000", 27 | value: []int{}, 28 | }, 29 | { 30 | name: "10000", 31 | value: []int{}, 32 | }, 33 | { 34 | name: "100000", 35 | value: []int{}, 36 | }, 37 | } 38 | 39 | func init() { 40 | for j := 0; j < len(tDuplicateBenchs); j++ { 41 | length, _ := strconv.Atoi(tDuplicateBenchs[j].name) 42 | for i := 0; i < length/10; i++ { 43 | tDuplicateBenchs[j].value = append(tDuplicateBenchs[j].value.([]int), 0, 1, 2, 3, 4, 5, 6, 7, 8, 9) 44 | } 45 | } 46 | } 47 | 48 | func TestDuplicate(t *testing.T) { 49 | var tests = []TDuplicate{ 50 | { 51 | name: "nil", 52 | value: nil, 53 | }, 54 | { 55 | name: "empty", 56 | value: []int{}, 57 | }, 58 | { 59 | name: "int", 60 | value: 15, 61 | }, 62 | { 63 | name: "float64", 64 | value: 15.1, 65 | }, 66 | { 67 | name: "array", 68 | value: [3]int{1, 2, 3}, 69 | }, 70 | { 71 | name: "slice", 72 | value: []int{1, 2, 3}, 73 | }, 74 | { 75 | name: "struct", 76 | value: map[string]string{"1": "s", "2": "x"}, 77 | }, 78 | { 79 | name: "complex", 80 | value: map[string]interface{}{"1": map[string]string{"1": "1"}, "2": []int{1}}, 81 | }, 82 | } 83 | 84 | for _, subject := range tests { 85 | t.Run(subject.name, func(t *testing.T) { 86 | defer internal.DeferTestCases(t, subject.value) 87 | got := Duplicate(subject.value) 88 | 89 | if !Same(got, subject.value) { 90 | t.Errorf("got = %v, wanted = %v", got, subject.value) 91 | } 92 | }) 93 | } 94 | } 95 | 96 | func BenchmarkDuplicate(b *testing.B) { 97 | for j := 0; j < len(tDuplicateBenchs); j++ { 98 | b.Run(fmt.Sprintf("slice_size_%s", tDuplicateBenchs[j].name), func(b *testing.B) { 99 | for i := 0; i < b.N; i++ { 100 | Duplicate(tDuplicateBenchs[j].value) 101 | } 102 | }) 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /generals/same.go: -------------------------------------------------------------------------------- 1 | package generals 2 | 3 | import "reflect" 4 | 5 | // Determines if passed variables are exactly the same in terms of 6 | // values and their types. 7 | // 8 | // Worst-Case Complexity: O(n) 9 | // 10 | // Best-Case Complexity: O(1) 11 | // 12 | // n = length of value1 or value2(if they have the same length, else n = 1) 13 | func Same(value1 interface{}, value2 interface{}) (condition bool) { 14 | condition = true 15 | v1 := reflect.ValueOf(value1) 16 | v2 := reflect.ValueOf(value2) 17 | 18 | // Check for nil and "" and other zero values 19 | if (!v1.IsValid() && !v2.IsValid()) && (v1.Kind() == v2.Kind()) { 20 | return 21 | } 22 | 23 | if v1.Kind() != v2.Kind() { 24 | if v1.Kind() == reflect.Interface && v2.Kind() != reflect.Interface { 25 | if v1.CanConvert(v2.Type()) { 26 | v1 = v1.Convert(v2.Type()) 27 | } 28 | } else if v2.Kind() == reflect.Interface && v1.Kind() != reflect.Interface { 29 | if v2.CanConvert(v1.Type()) { 30 | v2 = v2.Convert(v1.Type()) 31 | } 32 | } 33 | if v1.Kind() == v2.Kind() && v1.Interface() == v2.Interface() { 34 | return 35 | } 36 | condition = false 37 | return 38 | } 39 | 40 | defer func() { 41 | if r := recover(); r != nil { 42 | condition = false 43 | } 44 | }() 45 | 46 | switch v1.Kind() { 47 | case reflect.Array, reflect.Slice: 48 | if v1.Len() != v2.Len() { 49 | condition = false 50 | return 51 | } 52 | for i := 0; i < v1.Len(); i++ { 53 | condition = Same(v1.Index(i).Interface(), v2.Index(i).Interface()) 54 | if !condition { 55 | condition = false 56 | return 57 | } 58 | } 59 | case reflect.Map: 60 | if v1.Len() != v2.Len() { 61 | condition = false 62 | return 63 | } 64 | 65 | if len(v1.MapKeys()) != len(v2.MapKeys()) { 66 | condition = false 67 | return 68 | } 69 | 70 | keys := v1.MapKeys() 71 | for i := 0; i < len(v1.MapKeys()); i = i + 1 { 72 | value1 := v1.MapIndex(keys[i]) 73 | value2 := v2.MapIndex(keys[i]) 74 | if !value1.IsValid() { 75 | condition = false 76 | return 77 | } 78 | if !value2.IsValid() { 79 | condition = false 80 | return 81 | } 82 | 83 | if condition = Same(value1.Interface(), value2.Interface()); !condition { 84 | condition = false 85 | return 86 | } 87 | } 88 | case reflect.Struct: 89 | condition = value1 == value2 90 | case reflect.Ptr: 91 | condition = Same(v1.Elem().Interface(), v2.Elem().Interface()) 92 | default: 93 | if v1.Interface() != v2.Interface() { 94 | condition = false 95 | } 96 | } 97 | 98 | return 99 | } 100 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/golodash/godash 2 | 3 | go 1.17 4 | 5 | require github.com/jinzhu/copier v0.3.5 // indirect 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= 2 | github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= 3 | -------------------------------------------------------------------------------- /images/golang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golodash/godash/d43d3d69e09ad57ff0b325906d6eaa6233779e2f/images/golang.png -------------------------------------------------------------------------------- /images/table-of-contents.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golodash/godash/d43d3d69e09ad57ff0b325906d6eaa6233779e2f/images/table-of-contents.png -------------------------------------------------------------------------------- /maths/add.go: -------------------------------------------------------------------------------- 1 | package maths 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Adds two numbers. 10 | // 11 | // Complexity: O(1) 12 | func Add(number1, number2 interface{}) interface{} { 13 | if !internal.IsNumber(number1) { 14 | panic("'number1' is not a number") 15 | } 16 | if !internal.IsNumber(number2) { 17 | panic("'number2' is not a number") 18 | } 19 | 20 | outputType := internal.GetOutputNumberType(number1, number2) 21 | 22 | number1Value := reflect.ValueOf(number1) 23 | number2Value := reflect.ValueOf(number2) 24 | 25 | if number1Value.Kind() != outputType.Kind() { 26 | number1Value = number1Value.Convert(outputType) 27 | } else if number2Value.Kind() != outputType.Kind() { 28 | number2Value = number2Value.Convert(outputType) 29 | } 30 | 31 | output := reflect.Zero(outputType) 32 | if internal.CanFloat(number1Value.Interface()) { 33 | var temp float64 = 0 34 | temp = number1Value.Convert(reflect.TypeOf(temp)).Float() + number2Value.Convert(reflect.TypeOf(temp)).Float() 35 | output = reflect.ValueOf(temp).Convert(outputType) 36 | } else if internal.CanUint(number1Value.Interface()) { 37 | var temp uint64 = 0 38 | temp = number1Value.Convert(reflect.TypeOf(temp)).Uint() + number2Value.Convert(reflect.TypeOf(temp)).Uint() 39 | output = reflect.ValueOf(temp).Convert(outputType) 40 | } else if internal.CanInt(number1Value.Interface()) { 41 | var temp int64 = 0 42 | temp = number1Value.Convert(reflect.TypeOf(temp)).Int() + number2Value.Convert(reflect.TypeOf(temp)).Int() 43 | output = reflect.ValueOf(temp).Convert(outputType) 44 | } 45 | 46 | return output.Interface() 47 | } 48 | -------------------------------------------------------------------------------- /maths/add_test.go: -------------------------------------------------------------------------------- 1 | package maths 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/golodash/godash/generals" 7 | "github.com/golodash/godash/internal" 8 | ) 9 | 10 | type TAdd struct { 11 | name string 12 | number1 interface{} 13 | number2 interface{} 14 | want interface{} 15 | } 16 | 17 | var tAddBench = TAdd{ 18 | number1: 0.00026546597, 19 | number2: 4567, 20 | } 21 | 22 | func TestAdd(t *testing.T) { 23 | var tests = []TAdd{ 24 | { 25 | name: "nil", 26 | number1: nil, 27 | number2: nil, 28 | want: nil, 29 | }, 30 | { 31 | name: "empty", 32 | number1: 0, 33 | number2: 0, 34 | want: 0, 35 | }, 36 | { 37 | name: "int int", 38 | number1: 15, 39 | number2: 3, 40 | want: 18, 41 | }, 42 | { 43 | name: "int float64", 44 | number1: 15, 45 | number2: 3.231, 46 | want: 18.231, 47 | }, 48 | { 49 | name: "float64 float64", 50 | number1: 15.1, 51 | number2: 3.231, 52 | want: 18.331, 53 | }, 54 | { 55 | name: "uint8 uint16", 56 | number1: uint8(3), 57 | number2: uint16(15), 58 | want: uint16(18), 59 | }, 60 | { 61 | name: "int8 uint16", 62 | number1: int8(3), 63 | number2: uint16(15), 64 | want: uint16(18), 65 | }, 66 | } 67 | 68 | for _, subject := range tests { 69 | t.Run(subject.name, func(t *testing.T) { 70 | defer internal.DeferTestCases(t, subject.want) 71 | got := Add(subject.number1, subject.number2) 72 | 73 | if !generals.Same(got, subject.want) { 74 | t.Errorf("got = %v, wanted = %v", got, subject.want) 75 | return 76 | } 77 | }) 78 | } 79 | } 80 | 81 | func BenchmarkAdd(b *testing.B) { 82 | for i := 0; i < b.N; i++ { 83 | Add(tAddBench.number1, tAddBench.number2) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /maths/ceil.go: -------------------------------------------------------------------------------- 1 | package maths 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Computes number rounded up to precision. 10 | // 11 | // Complexity: O(1) 12 | func Ceil(input interface{}, precision int) interface{} { 13 | if !internal.IsNumber(input) { 14 | panic("'input' is not a number") 15 | } 16 | 17 | inputValue := reflect.ValueOf(input) 18 | inputType := inputValue.Type() 19 | 20 | isNegative := false 21 | if internal.CanInt(input) && inputValue.Int() < 0 { 22 | isNegative = true 23 | inputValue = reflect.ValueOf(-inputValue.Int()) 24 | } else if internal.CanFloat(input) && inputValue.Float() < 0 { 25 | isNegative = true 26 | inputValue = reflect.ValueOf(-inputValue.Float()) 27 | } 28 | 29 | if precision > 0 { 30 | floatValue := inputValue.Convert(reflect.TypeOf(0.1)).Float() 31 | tenPowered := Power(10.0, precision).(float64) 32 | floatValuePowered := floatValue * tenPowered 33 | output := float64(int(floatValuePowered)+1) / tenPowered 34 | 35 | if isNegative { 36 | output = -output 37 | } 38 | return reflect.ValueOf(output).Convert(inputType).Interface() 39 | } else if precision < 0 { 40 | precision = -precision 41 | intValue := inputValue.Convert(reflect.TypeOf(0)).Int() 42 | output := intValue + int64(Power(10, precision).(int)) - intValue%int64((Power(10, precision).(int))) 43 | 44 | if isNegative { 45 | output = -output 46 | } 47 | return reflect.ValueOf(output).Convert(inputType).Interface() 48 | } else { 49 | output := inputValue.Convert(reflect.TypeOf(0)).Int() + int64(1) 50 | 51 | if isNegative { 52 | output = -output 53 | } 54 | return reflect.ValueOf(output).Convert(inputType).Interface() 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /maths/ceil_test.go: -------------------------------------------------------------------------------- 1 | package maths 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/golodash/godash/generals" 7 | "github.com/golodash/godash/internal" 8 | ) 9 | 10 | type TCeil struct { 11 | name string 12 | input interface{} 13 | precision int 14 | want interface{} 15 | } 16 | 17 | var tCeilBench = TCeil{ 18 | input: 0.00026546597, 19 | precision: 5, 20 | } 21 | 22 | func TestCeil(t *testing.T) { 23 | var tests = []TCeil{ 24 | { 25 | name: "nil", 26 | input: nil, 27 | precision: 5, 28 | want: nil, 29 | }, 30 | { 31 | name: "empty", 32 | input: 0, 33 | precision: 0, 34 | want: 1, 35 | }, 36 | { 37 | name: "positive precision", 38 | input: 0.123456, 39 | precision: 3, 40 | want: 0.124, 41 | }, 42 | { 43 | name: "negative precision", 44 | input: 1532, 45 | precision: -3, 46 | want: 2000, 47 | }, 48 | { 49 | name: "zero precision", 50 | input: 1532.321, 51 | precision: 0, 52 | want: 1533.0, 53 | }, 54 | { 55 | name: "positive precision, negative value", 56 | input: -0.123456, 57 | precision: 3, 58 | want: -0.124, 59 | }, 60 | { 61 | name: "negative precision, negative value", 62 | input: -1532, 63 | precision: -3, 64 | want: -2000, 65 | }, 66 | { 67 | name: "zero precision, negative value", 68 | input: -1532.321, 69 | precision: 0, 70 | want: -1533.0, 71 | }, 72 | { 73 | name: "normal-float32 type", 74 | input: float32(-1532.321), 75 | precision: 2, 76 | want: float32(-1532.33), 77 | }, 78 | } 79 | 80 | for _, subject := range tests { 81 | t.Run(subject.name, func(t *testing.T) { 82 | defer internal.DeferTestCases(t, subject.want) 83 | got := Ceil(subject.input, subject.precision) 84 | 85 | if !generals.Same(got, subject.want) { 86 | t.Errorf("got = %v, wanted = %v", got, subject.want) 87 | return 88 | } 89 | }) 90 | } 91 | } 92 | 93 | func BenchmarkCeil(b *testing.B) { 94 | for i := 0; i < b.N; i++ { 95 | Ceil(tCeilBench.input, tCeilBench.precision) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /maths/divide.go: -------------------------------------------------------------------------------- 1 | package maths 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Divides two numbers. 10 | // 11 | // Complexity: O(1) 12 | func Divide(number1 interface{}, number2 interface{}) interface{} { 13 | if !internal.IsNumber(number1) { 14 | panic("'number1' is not a number") 15 | } 16 | if !internal.IsNumber(number2) { 17 | panic("'number2' is not a number") 18 | } 19 | if number2 == 0 { 20 | panic("'number2' can not be 0") 21 | } 22 | 23 | outputType := internal.GetOutputNumberType(number1, number2) 24 | 25 | number1Value := reflect.ValueOf(number1) 26 | number2Value := reflect.ValueOf(number2) 27 | 28 | if number1Value.Kind() != outputType.Kind() { 29 | number1Value = number1Value.Convert(outputType) 30 | } else if number2Value.Kind() != outputType.Kind() { 31 | number2Value = number2Value.Convert(outputType) 32 | } 33 | 34 | output := reflect.Zero(outputType) 35 | if internal.CanFloat(number1Value.Interface()) { 36 | var temp float64 = 0 37 | temp = number1Value.Convert(reflect.TypeOf(temp)).Float() / number2Value.Convert(reflect.TypeOf(temp)).Float() 38 | output = reflect.ValueOf(temp).Convert(outputType) 39 | } else if internal.CanUint(number1Value.Interface()) { 40 | var temp uint64 = 0 41 | temp = number1Value.Convert(reflect.TypeOf(temp)).Uint() / number2Value.Convert(reflect.TypeOf(temp)).Uint() 42 | output = reflect.ValueOf(temp).Convert(outputType) 43 | } else if internal.CanInt(number1Value.Interface()) { 44 | var temp int64 = 0 45 | temp = number1Value.Convert(reflect.TypeOf(temp)).Int() / number2Value.Convert(reflect.TypeOf(temp)).Int() 46 | output = reflect.ValueOf(temp).Convert(outputType) 47 | } 48 | 49 | return output.Interface() 50 | } 51 | -------------------------------------------------------------------------------- /maths/divide_test.go: -------------------------------------------------------------------------------- 1 | package maths 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/golodash/godash/generals" 7 | "github.com/golodash/godash/internal" 8 | ) 9 | 10 | type TDivide struct { 11 | name string 12 | number1 interface{} 13 | number2 interface{} 14 | want interface{} 15 | } 16 | 17 | var tDivideBench = TDivide{ 18 | number1: 0.00026546597, 19 | number2: 4567, 20 | } 21 | 22 | func TestDivide(t *testing.T) { 23 | var tests = []TDivide{ 24 | { 25 | name: "nil", 26 | number1: nil, 27 | number2: nil, 28 | want: nil, 29 | }, 30 | { 31 | name: "empty", 32 | number1: 0, 33 | number2: 0, 34 | want: nil, 35 | }, 36 | { 37 | name: "int int", 38 | number1: 15, 39 | number2: 3, 40 | want: 5, 41 | }, 42 | { 43 | name: "int float64", 44 | number1: 15, 45 | number2: 1.5, 46 | want: 10.0, 47 | }, 48 | { 49 | name: "float64 float64", 50 | number1: 4.5, 51 | number2: 1.5, 52 | want: 3.0, 53 | }, 54 | { 55 | name: "uint8 uint16", 56 | number1: uint8(15), 57 | number2: uint16(3), 58 | want: uint16(5), 59 | }, 60 | { 61 | name: "int8 uint16", 62 | number1: int8(15), 63 | number2: uint16(3), 64 | want: uint16(5), 65 | }, 66 | } 67 | 68 | for _, subject := range tests { 69 | t.Run(subject.name, func(t *testing.T) { 70 | defer internal.DeferTestCases(t, subject.want) 71 | got := Divide(subject.number1, subject.number2) 72 | 73 | if !generals.Same(got, subject.want) { 74 | t.Errorf("got = %v, wanted = %v", got, subject.want) 75 | return 76 | } 77 | }) 78 | } 79 | } 80 | 81 | func BenchmarkDivide(b *testing.B) { 82 | for i := 0; i < b.N; i++ { 83 | Divide(tDivideBench.number1, tDivideBench.number2) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /maths/floor.go: -------------------------------------------------------------------------------- 1 | package maths 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Computes number rounded down to precision. 10 | // 11 | // Complexity: O(1) 12 | func Floor(input interface{}, precision int) interface{} { 13 | if !internal.IsNumber(input) { 14 | panic("'input' is not a number") 15 | } 16 | 17 | inputValue := reflect.ValueOf(input) 18 | inputType := inputValue.Type() 19 | 20 | isNegative := false 21 | if internal.CanInt(input) && inputValue.Int() < 0 { 22 | isNegative = true 23 | inputValue = reflect.ValueOf(-inputValue.Int()) 24 | } else if internal.CanFloat(input) && inputValue.Float() < 0 { 25 | isNegative = true 26 | inputValue = reflect.ValueOf(-inputValue.Float()) 27 | } 28 | 29 | if precision > 0 { 30 | floatValue := inputValue.Convert(reflect.TypeOf(0.1)).Float() 31 | tenPowered := Power(10.0, precision).(float64) 32 | floatValuePowered := floatValue * tenPowered 33 | output := float64(int(floatValuePowered)) / tenPowered 34 | 35 | if isNegative { 36 | output = -output 37 | } 38 | return reflect.ValueOf(output).Convert(inputType).Interface() 39 | } else if precision < 0 { 40 | precision = -precision 41 | intValue := inputValue.Convert(reflect.TypeOf(0)).Int() 42 | output := intValue - intValue%int64(Power(10, precision).(int)) 43 | 44 | if isNegative { 45 | output = -output 46 | } 47 | return reflect.ValueOf(output).Convert(inputType).Interface() 48 | } else { 49 | output := inputValue.Convert(reflect.TypeOf(0)).Int() 50 | 51 | if isNegative { 52 | output = -output 53 | } 54 | return reflect.ValueOf(output).Convert(inputType).Interface() 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /maths/floor_test.go: -------------------------------------------------------------------------------- 1 | package maths 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/golodash/godash/generals" 7 | "github.com/golodash/godash/internal" 8 | ) 9 | 10 | type TFloor struct { 11 | name string 12 | input interface{} 13 | precision int 14 | want interface{} 15 | } 16 | 17 | var tFloorBench = TFloor{ 18 | input: 0.00026546597, 19 | precision: 5, 20 | } 21 | 22 | func TestFloor(t *testing.T) { 23 | var tests = []TFloor{ 24 | { 25 | name: "nil", 26 | input: nil, 27 | precision: 5, 28 | want: nil, 29 | }, 30 | { 31 | name: "empty", 32 | input: 0, 33 | precision: 0, 34 | want: 0, 35 | }, 36 | { 37 | name: "positive precision", 38 | input: 0.123456, 39 | precision: 3, 40 | want: 0.123, 41 | }, 42 | { 43 | name: "negative precision", 44 | input: 1532, 45 | precision: -3, 46 | want: 1000, 47 | }, 48 | { 49 | name: "zero precision", 50 | input: 1532.321, 51 | precision: 0, 52 | want: 1532.0, 53 | }, 54 | { 55 | name: "positive precision, negative value", 56 | input: -0.123456, 57 | precision: 3, 58 | want: -0.123, 59 | }, 60 | { 61 | name: "negative precision, negative value", 62 | input: -1532, 63 | precision: -3, 64 | want: -1000, 65 | }, 66 | { 67 | name: "zero precision, negative value", 68 | input: -1532.321, 69 | precision: 0, 70 | want: -1532.0, 71 | }, 72 | { 73 | name: "normal-float32 type", 74 | input: float32(-1532.321), 75 | precision: 2, 76 | want: float32(-1532.32), 77 | }, 78 | } 79 | 80 | for _, subject := range tests { 81 | t.Run(subject.name, func(t *testing.T) { 82 | defer internal.DeferTestCases(t, subject.want) 83 | got := Floor(subject.input, subject.precision) 84 | 85 | if !generals.Same(got, subject.want) { 86 | t.Errorf("got = %v, wanted = %v", got, subject.want) 87 | return 88 | } 89 | }) 90 | } 91 | } 92 | 93 | func BenchmarkFloor(b *testing.B) { 94 | for i := 0; i < b.N; i++ { 95 | Floor(tFloorBench.input, tFloorBench.precision) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /maths/multiply.go: -------------------------------------------------------------------------------- 1 | package maths 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Multiplies two numbers. 10 | // 11 | // Complexity: O(1) 12 | func Multiply(number1, number2 interface{}) interface{} { 13 | if !internal.IsNumber(number1) { 14 | panic("'number1' is not a number") 15 | } 16 | if !internal.IsNumber(number2) { 17 | panic("'number2' is not a number") 18 | } 19 | 20 | outputType := internal.GetOutputNumberType(number1, number2) 21 | 22 | number1Value := reflect.ValueOf(number1) 23 | number2Value := reflect.ValueOf(number2) 24 | 25 | if number1Value.Kind() != outputType.Kind() { 26 | number1Value = number1Value.Convert(outputType) 27 | } else if number2Value.Kind() != outputType.Kind() { 28 | number2Value = number2Value.Convert(outputType) 29 | } 30 | 31 | output := reflect.Zero(outputType) 32 | if internal.CanFloat(number1Value.Interface()) { 33 | var temp float64 = 0 34 | temp = number1Value.Convert(reflect.TypeOf(temp)).Float() * number2Value.Convert(reflect.TypeOf(temp)).Float() 35 | output = reflect.ValueOf(temp).Convert(outputType) 36 | } else if internal.CanInt(number1Value.Interface()) { 37 | var temp int64 = 0 38 | temp = number1Value.Convert(reflect.TypeOf(temp)).Int() * number2Value.Convert(reflect.TypeOf(temp)).Int() 39 | output = reflect.ValueOf(temp).Convert(outputType) 40 | } else if internal.CanUint(number1Value.Interface()) { 41 | var temp uint64 = 0 42 | temp = number1Value.Convert(reflect.TypeOf(temp)).Uint() * number2Value.Convert(reflect.TypeOf(temp)).Uint() 43 | output = reflect.ValueOf(temp).Convert(outputType) 44 | } 45 | 46 | return output.Interface() 47 | } 48 | -------------------------------------------------------------------------------- /maths/multiply_test.go: -------------------------------------------------------------------------------- 1 | package maths 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/golodash/godash/generals" 7 | "github.com/golodash/godash/internal" 8 | ) 9 | 10 | type TMultiply struct { 11 | name string 12 | number1 interface{} 13 | number2 interface{} 14 | want interface{} 15 | } 16 | 17 | var tMultiplyBench = TMultiply{ 18 | number1: 0.00026546597, 19 | number2: 4567, 20 | } 21 | 22 | func TestMultiply(t *testing.T) { 23 | var tests = []TMultiply{ 24 | { 25 | name: "nil", 26 | number1: nil, 27 | number2: nil, 28 | want: nil, 29 | }, 30 | { 31 | name: "empty", 32 | number1: 0, 33 | number2: 0, 34 | want: 0, 35 | }, 36 | { 37 | name: "int int", 38 | number1: 3, 39 | number2: 3, 40 | want: 9, 41 | }, 42 | { 43 | name: "int float64", 44 | number1: 15, 45 | number2: 3.1, 46 | want: 46.5, 47 | }, 48 | { 49 | name: "float64 float64", 50 | number1: 15.1, 51 | number2: 3.231, 52 | want: 48.7881, 53 | }, 54 | { 55 | name: "uint8 uint16", 56 | number1: uint8(3), 57 | number2: uint16(3), 58 | want: uint16(9), 59 | }, 60 | { 61 | name: "int8 uint16", 62 | number1: int8(3), 63 | number2: uint16(3), 64 | want: uint16(9), 65 | }, 66 | } 67 | 68 | for _, subject := range tests { 69 | t.Run(subject.name, func(t *testing.T) { 70 | defer internal.DeferTestCases(t, subject.want) 71 | got := Multiply(subject.number1, subject.number2) 72 | 73 | if !generals.Same(got, subject.want) { 74 | t.Errorf("got = %v, wanted = %v", got, subject.want) 75 | return 76 | } 77 | }) 78 | } 79 | } 80 | 81 | func BenchmarkMultiply(b *testing.B) { 82 | for i := 0; i < b.N; i++ { 83 | Multiply(tMultiplyBench.number1, tMultiplyBench.number2) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /maths/power.go: -------------------------------------------------------------------------------- 1 | package maths 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Returns input to the power of number. 10 | // 11 | // Complexity: O(number) 12 | func Power(input interface{}, number int) interface{} { 13 | if !internal.IsNumber(input) { 14 | panic("'input' is not a number") 15 | } 16 | 17 | if number == 0 { 18 | return 1 19 | } 20 | 21 | inputFloat := reflect.ValueOf(input).Convert(reflect.TypeOf(0.1)).Float() 22 | inputType := reflect.TypeOf(input) 23 | 24 | isNegative := false 25 | if inputFloat < 0 { 26 | isNegative = true 27 | inputFloat = -inputFloat 28 | } 29 | isNegativeNumber := false 30 | if number < 0 { 31 | isNegativeNumber = true 32 | number = -number 33 | } 34 | 35 | output := inputFloat 36 | for i := 1; i < number; i++ { 37 | output *= inputFloat 38 | } 39 | 40 | if isNegative && number%2 != 0 { 41 | output = -output 42 | } 43 | if isNegativeNumber { 44 | output = 1.0 / output 45 | if output != float64(int(output)) { 46 | return output 47 | } 48 | } 49 | 50 | return reflect.ValueOf(output).Convert(inputType).Interface() 51 | } 52 | -------------------------------------------------------------------------------- /maths/round.go: -------------------------------------------------------------------------------- 1 | package maths 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Computes number rounded to precision. 10 | // 11 | // Complexity: O(1) 12 | func Round(number interface{}, precision int) interface{} { 13 | if !internal.IsNumber(number) { 14 | panic("'number' is not a number") 15 | } 16 | 17 | numberValue := reflect.ValueOf(number) 18 | floatNumber := numberValue.Convert(reflect.TypeOf(1.0)).Float() 19 | tenPowered := reflect.ValueOf(Power(10.0, precision+1)).Float() 20 | comparator := int(floatNumber*tenPowered) % 10 21 | if comparator < 0 { 22 | comparator = -comparator 23 | } 24 | if comparator >= 5 { 25 | return Ceil(number, precision) 26 | } else { 27 | return Floor(number, precision) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /maths/subtract.go: -------------------------------------------------------------------------------- 1 | package maths 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Subtracts two numbers. 10 | // 11 | // Complexity: O(1) 12 | func Subtract(number1, number2 interface{}) interface{} { 13 | if !internal.IsNumber(number1) { 14 | panic("'number1' is not a number") 15 | } 16 | if !internal.IsNumber(number2) { 17 | panic("'number2' is not a number") 18 | } 19 | 20 | outputType := internal.GetOutputNumberType(number1, number2) 21 | 22 | number1Value := reflect.ValueOf(number1) 23 | number2Value := reflect.ValueOf(number2) 24 | 25 | if number1Value.Kind() != outputType.Kind() { 26 | number1Value = number1Value.Convert(outputType) 27 | } else if number2Value.Kind() != outputType.Kind() { 28 | number2Value = number2Value.Convert(outputType) 29 | } 30 | 31 | output := reflect.Zero(outputType) 32 | if internal.CanFloat(number1Value.Interface()) { 33 | var temp float64 = 0 34 | temp = number1Value.Convert(reflect.TypeOf(temp)).Float() - number2Value.Convert(reflect.TypeOf(temp)).Float() 35 | output = reflect.ValueOf(temp).Convert(outputType) 36 | } else if internal.CanUint(number1Value.Interface()) { 37 | var temp uint64 = 0 38 | temp = number1Value.Convert(reflect.TypeOf(temp)).Uint() - number2Value.Convert(reflect.TypeOf(temp)).Uint() 39 | output = reflect.ValueOf(temp).Convert(outputType) 40 | } else if internal.CanInt(number1Value.Interface()) { 41 | var temp int64 = 0 42 | temp = number1Value.Convert(reflect.TypeOf(temp)).Int() - number2Value.Convert(reflect.TypeOf(temp)).Int() 43 | output = reflect.ValueOf(temp).Convert(outputType) 44 | } 45 | 46 | return output.Interface() 47 | } 48 | -------------------------------------------------------------------------------- /maths/subtract_test.go: -------------------------------------------------------------------------------- 1 | package maths 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/golodash/godash/generals" 7 | "github.com/golodash/godash/internal" 8 | ) 9 | 10 | type TSubtract struct { 11 | name string 12 | number1 interface{} 13 | number2 interface{} 14 | want interface{} 15 | } 16 | 17 | var tSubtractBench = TSubtract{ 18 | number1: 0.00026546597, 19 | number2: 4567, 20 | } 21 | 22 | func TestSubtract(t *testing.T) { 23 | var tests = []TSubtract{ 24 | { 25 | name: "nil", 26 | number1: nil, 27 | number2: nil, 28 | want: nil, 29 | }, 30 | { 31 | name: "empty", 32 | number1: 0, 33 | number2: 0, 34 | want: 0, 35 | }, 36 | { 37 | name: "int int", 38 | number1: 15, 39 | number2: 3, 40 | want: 12, 41 | }, 42 | { 43 | name: "int float64", 44 | number1: 3, 45 | number2: 1.231, 46 | want: 1.769, 47 | }, 48 | { 49 | name: "float64 float64", 50 | number1: 15.1, 51 | number2: 3.231, 52 | want: 11.869, 53 | }, 54 | { 55 | name: "uint8 uint16", 56 | number1: uint8(15), 57 | number2: uint16(3), 58 | want: uint16(12), 59 | }, 60 | { 61 | name: "int8 uint16", 62 | number1: int8(15), 63 | number2: uint16(3), 64 | want: uint16(12), 65 | }, 66 | } 67 | 68 | for _, subject := range tests { 69 | t.Run(subject.name, func(t *testing.T) { 70 | defer internal.DeferTestCases(t, subject.want) 71 | got := Subtract(subject.number1, subject.number2) 72 | 73 | if !generals.Same(got, subject.want) { 74 | t.Errorf("got = %v, wanted = %v", got, subject.want) 75 | return 76 | } 77 | }) 78 | } 79 | } 80 | 81 | func BenchmarkSubtract(b *testing.B) { 82 | for i := 0; i < b.N; i++ { 83 | Subtract(tSubtractBench.number1, tSubtractBench.number2) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /numbers/clamp.go: -------------------------------------------------------------------------------- 1 | package numbers 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Clamps number within the inclusive lower and upper bounds. 10 | // 11 | // Complexity: O(1) 12 | func Clamp(number, lower, upper interface{}) interface{} { 13 | if !internal.IsNumber(number) { 14 | panic("'number' is not a number") 15 | } 16 | if !internal.IsNumber(lower) { 17 | panic("'lower' is not a number") 18 | } 19 | if !internal.IsNumber(upper) { 20 | panic("'upper' is not a number") 21 | } 22 | 23 | numberFloat := reflect.ValueOf(number).Convert(reflect.TypeOf(1.0)).Float() 24 | lowerFloat := reflect.ValueOf(lower).Convert(reflect.TypeOf(1.0)).Float() 25 | upperFloat := reflect.ValueOf(upper).Convert(reflect.TypeOf(1.0)).Float() 26 | 27 | if lowerFloat > upperFloat { 28 | panic("'upper' has to be higher or equal to 'lower'") 29 | } 30 | 31 | outputType := internal.GetOutputNumberType(reflect.Zero(internal.GetOutputNumberType(number, lower)).Interface(), upper) 32 | 33 | if numberFloat <= lowerFloat { 34 | return reflect.ValueOf(lowerFloat).Convert(outputType).Interface() 35 | } else if numberFloat >= upperFloat { 36 | return reflect.ValueOf(upperFloat).Convert(outputType).Interface() 37 | } else { 38 | return reflect.ValueOf(numberFloat).Convert(outputType).Interface() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /numbers/clamp_test.go: -------------------------------------------------------------------------------- 1 | package numbers 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/golodash/godash/generals" 7 | "github.com/golodash/godash/internal" 8 | ) 9 | 10 | type TClamp struct { 11 | name string 12 | number interface{} 13 | lower interface{} 14 | upper interface{} 15 | want interface{} 16 | } 17 | 18 | var tClampBench = TClamp{ 19 | number: 243, 20 | lower: 1355, 21 | upper: 4325, 22 | } 23 | 24 | func TestClamp(t *testing.T) { 25 | var tests = []TClamp{ 26 | { 27 | name: "empty", 28 | number: 0, 29 | lower: 0, 30 | upper: 0, 31 | want: 0, 32 | }, 33 | { 34 | name: "int int int negative", 35 | number: -7, 36 | lower: -5, 37 | upper: -2, 38 | want: -5, 39 | }, 40 | { 41 | name: "int int int positive", 42 | number: 3, 43 | lower: 2, 44 | upper: 5, 45 | want: 3, 46 | }, 47 | { 48 | name: "float64 int int", 49 | number: 2.1, 50 | lower: 1, 51 | upper: 2, 52 | want: 2.0, 53 | }, 54 | { 55 | name: "float64 int int", 56 | number: 1.4, 57 | lower: 1, 58 | upper: 2, 59 | want: 1.4, 60 | }, 61 | { 62 | name: "int float64 float64", 63 | number: 2, 64 | lower: 1.2, 65 | upper: 2.5, 66 | want: 2.0, 67 | }, 68 | { 69 | name: "int float64 float64", 70 | number: 1, 71 | lower: 1.2, 72 | upper: 2.5, 73 | want: 1.2, 74 | }, 75 | { 76 | name: "float64 float64 float64", 77 | number: 0.3, 78 | lower: 1.1, 79 | upper: 1.3, 80 | want: 1.1, 81 | }, 82 | { 83 | name: "float64 float64 float64", 84 | number: 1.2, 85 | lower: 1.1, 86 | upper: 1.3, 87 | want: 1.2, 88 | }, 89 | } 90 | 91 | for _, subject := range tests { 92 | t.Run(subject.name, func(t *testing.T) { 93 | defer internal.DeferTestCases(t, subject.want) 94 | got := Clamp(subject.number, subject.lower, subject.upper) 95 | 96 | if !generals.Same(got, subject.want) { 97 | t.Errorf("got = %v, wanted = %v", got, subject.want) 98 | return 99 | } 100 | }) 101 | } 102 | } 103 | 104 | func BenchmarkClamp(b *testing.B) { 105 | for i := 0; i < b.N; i++ { 106 | Clamp(tClampBench.number, tClampBench.lower, tClampBench.upper) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /numbers/in_range.go: -------------------------------------------------------------------------------- 1 | package numbers 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Check if number is lower than 'upper' and greater than 'lower'. 10 | // 11 | // Complexity: O(1) 12 | func InRange(number, lower, upper interface{}) bool { 13 | if !internal.IsNumber(number) { 14 | panic("'number' is not a number") 15 | } 16 | if !internal.IsNumber(lower) { 17 | panic("'start' is not a number") 18 | } 19 | if !internal.IsNumber(upper) { 20 | panic("'end' is not a number") 21 | } 22 | 23 | numberFloat := reflect.ValueOf(number).Convert(reflect.TypeOf(1.0)).Float() 24 | lowerFloat := reflect.ValueOf(lower).Convert(reflect.TypeOf(1.0)).Float() 25 | upperFloat := reflect.ValueOf(upper).Convert(reflect.TypeOf(1.0)).Float() 26 | 27 | if lowerFloat > upperFloat { 28 | panic("'upper' has to be higher or equal to 'lower'") 29 | } 30 | 31 | if numberFloat >= lowerFloat && numberFloat <= upperFloat { 32 | return true 33 | } else { 34 | return false 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /numbers/in_range_test.go: -------------------------------------------------------------------------------- 1 | package numbers 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/golodash/godash/generals" 7 | "github.com/golodash/godash/internal" 8 | ) 9 | 10 | type TInRange struct { 11 | name string 12 | number interface{} 13 | start interface{} 14 | end interface{} 15 | want bool 16 | } 17 | 18 | var tInRangeBench = TInRange{ 19 | number: 1200, 20 | start: 1355, 21 | end: 4325, 22 | } 23 | 24 | func TestInRange(t *testing.T) { 25 | var tests = []TInRange{ 26 | { 27 | name: "empty", 28 | number: 0, 29 | start: 0, 30 | end: 0, 31 | want: true, 32 | }, 33 | { 34 | name: "int int int", 35 | number: -4, 36 | start: -5, 37 | end: -2, 38 | want: true, 39 | }, 40 | { 41 | name: "int int int", 42 | number: -7, 43 | start: -5, 44 | end: -2, 45 | want: false, 46 | }, 47 | { 48 | name: "float64 int int", 49 | number: 2.1, 50 | start: 1, 51 | end: 2, 52 | want: false, 53 | }, 54 | { 55 | name: "int float64 float64", 56 | number: 2, 57 | start: 1.1, 58 | end: 4.6, 59 | want: true, 60 | }, 61 | { 62 | name: "int float64 float64", 63 | number: 1, 64 | start: 1.1, 65 | end: 4.6, 66 | want: false, 67 | }, 68 | { 69 | name: "float64 float64 float64", 70 | number: 0.3, 71 | start: 1.1, 72 | end: 1.3, 73 | want: false, 74 | }, 75 | { 76 | name: "float64 float64 float64", 77 | number: 1.2, 78 | start: 1.1, 79 | end: 1.3, 80 | want: true, 81 | }, 82 | } 83 | 84 | for _, subject := range tests { 85 | t.Run(subject.name, func(t *testing.T) { 86 | defer internal.DeferTestCases(t, subject.want) 87 | got := InRange(subject.number, subject.start, subject.end) 88 | 89 | if !generals.Same(got, subject.want) { 90 | t.Errorf("got = %v, wanted = %v", got, subject.want) 91 | return 92 | } 93 | }) 94 | } 95 | } 96 | 97 | func BenchmarkInRange(b *testing.B) { 98 | for i := 0; i < b.N; i++ { 99 | InRange(tInRangeBench.number, tInRangeBench.start, tInRangeBench.end) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /numbers/random.go: -------------------------------------------------------------------------------- 1 | package numbers 2 | 3 | import ( 4 | "math/rand" 5 | "reflect" 6 | "time" 7 | 8 | "github.com/golodash/godash/internal" 9 | ) 10 | 11 | // Produces a random number between the inclusive 'lower' and exclusive 'upper' bounds. 12 | // If floating is true, a floating-point number is returned instead of an integer. 13 | // 14 | // If floating is true, a float64 number type is returned. 15 | // If floating is false, an int64 number type is returned. 16 | // 17 | // Complexity: O(1) 18 | func Random(lower, upper interface{}, floating bool) interface{} { 19 | if !internal.IsNumber(lower) { 20 | panic("'lower' is not a number") 21 | } 22 | if !internal.IsNumber(upper) { 23 | panic("'upper' is not a number") 24 | } 25 | 26 | rand.Seed(time.Now().UnixNano()) 27 | 28 | lowerFloat := reflect.ValueOf(lower).Convert(reflect.TypeOf(1.0)).Float() 29 | upperFloat := reflect.ValueOf(upper).Convert(reflect.TypeOf(1.0)).Float() 30 | 31 | if lowerFloat > upperFloat { 32 | panic("'upper' has to be higher or equal to 'lower'") 33 | } 34 | 35 | output := lowerFloat + rand.Float64()*(upperFloat-lowerFloat) 36 | 37 | if !floating { 38 | return int64(output) 39 | } 40 | return output 41 | } 42 | -------------------------------------------------------------------------------- /numbers/random_test.go: -------------------------------------------------------------------------------- 1 | package numbers 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/golodash/godash/generals" 7 | "github.com/golodash/godash/internal" 8 | ) 9 | 10 | type TRandom struct { 11 | name string 12 | lower interface{} 13 | upper interface{} 14 | floating bool 15 | want interface{} 16 | } 17 | 18 | var tRandomBench = TRandom{ 19 | lower: 1355, 20 | upper: 4325, 21 | } 22 | 23 | func TestRandom(t *testing.T) { 24 | var tests = []TRandom{ 25 | { 26 | name: "empty", 27 | lower: 0, 28 | upper: 0, 29 | floating: false, 30 | want: int64(0), 31 | }, 32 | { 33 | name: "int int int", 34 | lower: 0, 35 | upper: 1, 36 | floating: false, 37 | want: int64(0), 38 | }, 39 | { 40 | name: "int int int non zero", 41 | lower: 1, 42 | upper: 1, 43 | floating: false, 44 | want: int64(1), 45 | }, 46 | } 47 | 48 | for _, subject := range tests { 49 | t.Run(subject.name, func(t *testing.T) { 50 | defer internal.DeferTestCases(t, subject.want) 51 | got := Random(subject.lower, subject.upper, subject.floating) 52 | 53 | if !generals.Same(got, subject.want) { 54 | t.Errorf("got = %v, wanted = %v", got, subject.want) 55 | return 56 | } 57 | }) 58 | } 59 | } 60 | 61 | func BenchmarkRandom(b *testing.B) { 62 | for i := 0; i < b.N; i++ { 63 | Random(tRandomBench.lower, tRandomBench.upper, tRandomBench.floating) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /slices/chunk.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Gets a slice and a size as input and splits the slice 10 | // into pieces in length of the size. 11 | // 12 | // Complexity: O(n) 13 | func Chunk(slice interface{}, size int) interface{} { 14 | if !internal.SliceCheck(slice) { 15 | panic("passed 'slice' variable is not slice type") 16 | } 17 | 18 | if size <= 0 { 19 | panic("size must be greater than zero") 20 | } 21 | 22 | var sliceValue = reflect.ValueOf(slice) 23 | var lenPieces int 24 | var length int = sliceValue.Len() 25 | if float32(length)/float32(size) != float32(length/size) { 26 | lenPieces = (length / size) + 1 27 | } else { 28 | lenPieces = length / size 29 | } 30 | 31 | var typeOfSlice = reflect.SliceOf(reflect.TypeOf(slice)) 32 | var chunks = reflect.MakeSlice(typeOfSlice, 0, lenPieces) 33 | var i int = size 34 | var j int = 0 35 | for ; i < length; i = i + size { 36 | chunks = reflect.Append(chunks, sliceValue.Slice(i-size, i)) 37 | j = j + 1 38 | } 39 | if length > i-size { 40 | chunks = reflect.Append(chunks, sliceValue.Slice(i-size, length)) 41 | } 42 | 43 | return chunks.Interface() 44 | } 45 | -------------------------------------------------------------------------------- /slices/compact.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/generals" 7 | "github.com/golodash/godash/internal" 8 | ) 9 | 10 | // Removes falsey items from slice except values you mentioned. 11 | // 12 | // Falsey items are {"", nil, 0, false} 13 | // 14 | // Complexity: O(n) 15 | func Compact(slice, excepts interface{}) interface{} { 16 | if !internal.SliceCheck(slice) { 17 | panic("passed 'slice' variable is not slice type") 18 | } 19 | 20 | exceptsValue := reflect.ValueOf(excepts) 21 | if exceptsValue.Kind() != reflect.Slice && excepts != nil { 22 | panic("just slice accepted as 'excepts' value") 23 | } 24 | if !exceptsValue.IsValid() { 25 | exceptsValue = reflect.MakeSlice(reflect.TypeOf([]interface{}{}), 0, 0) 26 | } 27 | 28 | defaultFalsey := []interface{}{"", nil, 0, false} 29 | falsey := []interface{}{} 30 | for i := 0; i < len(defaultFalsey); i++ { 31 | remain := true 32 | for j := 0; j < exceptsValue.Len(); j++ { 33 | if defaultFalsey[i] == exceptsValue.Index(j).Interface() { 34 | remain = false 35 | } 36 | } 37 | if remain { 38 | falsey = append(falsey, defaultFalsey[i]) 39 | } 40 | } 41 | 42 | sliceValue := reflect.ValueOf(slice) 43 | length := sliceValue.Len() 44 | result := reflect.MakeSlice(reflect.TypeOf(slice), 0, length) 45 | j := 0 46 | for i := 0; i < length; i++ { 47 | for k := 0; k < len(falsey); k++ { 48 | if generals.Same(sliceValue.Index(i).Interface(), falsey[k]) { 49 | if i == j { 50 | j = i + 1 51 | continue 52 | } 53 | result = reflect.AppendSlice(result, sliceValue.Slice(j, i)) 54 | j = i + 1 55 | } 56 | } 57 | } 58 | 59 | if j < sliceValue.Len() { 60 | result = reflect.AppendSlice(result, sliceValue.Slice(j, length)) 61 | } 62 | 63 | return result.Interface() 64 | } 65 | -------------------------------------------------------------------------------- /slices/concat.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Creates a new slice concatenating slice with other one. 10 | // 11 | // Complexity: O(n) 12 | // 13 | // n = number of 'values' length 14 | func Concat(slice, values interface{}) interface{} { 15 | if !internal.SliceCheck(slice) { 16 | panic("passed 'slice' variable is not slice type") 17 | } 18 | 19 | valuesValue := reflect.ValueOf(values) 20 | sliceValue := reflect.ValueOf(slice) 21 | sliceType := reflect.TypeOf(slice) 22 | for i := 0; i < valuesValue.Len(); i++ { 23 | item := reflect.ValueOf(valuesValue.Index(i).Interface()) 24 | if !item.IsValid() { 25 | continue 26 | } 27 | if item.Kind() == reflect.Slice { 28 | if sliceType.Kind() == item.Kind() || sliceType.Elem().Kind() == reflect.Interface || item.Elem().Kind() == reflect.Interface { 29 | for j := 0; j < item.Len(); j++ { 30 | innerItem := reflect.ValueOf(item.Index(j).Interface()) 31 | if !innerItem.IsValid() { 32 | continue 33 | } 34 | if innerItem.Kind() == sliceType.Elem().Kind() || sliceType.Elem().Kind() == reflect.Interface { 35 | sliceValue = reflect.Append(sliceValue, innerItem) 36 | } 37 | } 38 | } 39 | } else { 40 | if item.Kind() == sliceType.Elem().Kind() || sliceType.Elem().Kind() == reflect.Interface { 41 | sliceValue = reflect.Append(sliceValue, item) 42 | } 43 | } 44 | } 45 | 46 | return sliceValue.Interface() 47 | } 48 | -------------------------------------------------------------------------------- /slices/difference.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/generals" 7 | "github.com/golodash/godash/internal" 8 | ) 9 | 10 | // Returns a slice of 'slice' elements that are not included in the 11 | // other given slice using comparisons. 12 | // 13 | // Complexity: O(n*m) 14 | // 15 | // n = length of 'slice' 16 | // 17 | // m = length of 'notIncluded' 18 | func Difference(slice, notIncluded interface{}) interface{} { 19 | if !internal.SliceCheck(slice) { 20 | panic("passed 'slice' variable is not slice type") 21 | } 22 | if !internal.SliceCheck(notIncluded) { 23 | panic("passed 'notIncluded' variable is not slice type") 24 | } 25 | 26 | notInValue := reflect.ValueOf(notIncluded) 27 | sliceValue := reflect.ValueOf(slice) 28 | for i := sliceValue.Len() - 1; i > -1; i-- { 29 | if i >= sliceValue.Len() { 30 | continue 31 | } 32 | firstLoop: 33 | for j := 0; j < notInValue.Len(); j++ { 34 | if generals.Same(sliceValue.Index(i).Interface(), notInValue.Index(j).Interface()) { 35 | sliceValue = reflect.AppendSlice(sliceValue.Slice(0, i), sliceValue.Slice(i+1, sliceValue.Len())) 36 | i++ 37 | break firstLoop 38 | } 39 | } 40 | } 41 | 42 | return sliceValue.Interface() 43 | } 44 | -------------------------------------------------------------------------------- /slices/difference_by.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // This method is like 'difference' except that it accepts a custom function 10 | // which is invoked to compare elements of 'slice' to 'notIncluded' slice. 11 | // 12 | // function has to indicate if two given variables as inputs are equal or not. 13 | // 14 | // example for 'function': 15 | // 16 | // func isEqual(value1, value2 interface{}) bool { 17 | // return value1.(int) == value2.(int) 18 | // } 19 | // 20 | // Complexity: O(n*m) 21 | // 22 | // n = length of 'slice' 23 | // 24 | // m = length of 'notIncluded' 25 | func DifferenceBy(slice, notIncluded interface{}, function func(interface{}, interface{}) bool) interface{} { 26 | if !internal.SliceCheck(slice) { 27 | panic("passed 'slice' variable is not slice type") 28 | } 29 | if !internal.SliceCheck(notIncluded) { 30 | panic("passed 'notIncluded' variable is not slice type") 31 | } 32 | 33 | notInValue := reflect.ValueOf(notIncluded) 34 | sliceValue := reflect.ValueOf(slice) 35 | for i := sliceValue.Len() - 1; i > -1; i-- { 36 | if i >= sliceValue.Len() { 37 | continue 38 | } 39 | firstLoop: 40 | for j := 0; j < notInValue.Len(); j++ { 41 | if function(reflect.ValueOf(sliceValue.Index(i).Interface()).Interface(), reflect.ValueOf(notInValue.Index(j).Interface()).Interface()) { 42 | sliceValue = reflect.AppendSlice(sliceValue.Slice(0, i), sliceValue.Slice(i+1, sliceValue.Len())) 43 | i++ 44 | break firstLoop 45 | } 46 | } 47 | } 48 | 49 | return sliceValue.Interface() 50 | } 51 | -------------------------------------------------------------------------------- /slices/drop.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Creates a sub slice from passed slice with n elements dropped from the beginning. 10 | // 11 | // Complexity: O(1) 12 | func Drop(slice interface{}, n int) interface{} { 13 | if !internal.SliceCheck(slice) { 14 | panic("passed 'slice' variable is not slice type") 15 | } 16 | 17 | sliceValue := reflect.ValueOf(slice) 18 | if sliceValue.Len() < n { 19 | panic("'num' is bigger than slice length") 20 | } 21 | if n < 0 { 22 | panic("'num' is lower that zero") 23 | } 24 | 25 | return sliceValue.Slice(n, sliceValue.Len()).Interface() 26 | } 27 | -------------------------------------------------------------------------------- /slices/drop_by.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Creates a new slice from the passed slice and removes elements 10 | // from it when passed function returns true on that element. 11 | // 12 | // example for 'function': 13 | // 14 | // func isEqual(input interface{}) bool { 15 | // return input.(int)%2 == 0 16 | // } 17 | // 18 | // Complexity: O(n) 19 | func DropBy(slice interface{}, function func(interface{}) bool) interface{} { 20 | if !internal.SliceCheck(slice) { 21 | panic("passed 'slice' variable is not slice type") 22 | } 23 | 24 | sliceValue := reflect.ValueOf(slice) 25 | for i := 0; i < sliceValue.Len(); i++ { 26 | res := function(sliceValue.Index(i).Interface()) 27 | if res { 28 | sliceValue = reflect.AppendSlice(sliceValue.Slice(0, i), sliceValue.Slice(i+1, sliceValue.Len())) 29 | } 30 | } 31 | 32 | return sliceValue.Interface() 33 | } 34 | -------------------------------------------------------------------------------- /slices/drop_by_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TDropBy struct { 13 | name string 14 | arr interface{} 15 | want interface{} 16 | } 17 | 18 | var tDropByBenchs = []TDropBy{ 19 | { 20 | name: "10", 21 | arr: []int{}, 22 | }, 23 | { 24 | name: "100", 25 | arr: []int{}, 26 | }, 27 | { 28 | name: "1000", 29 | arr: []int{}, 30 | }, 31 | { 32 | name: "10000", 33 | arr: []int{}, 34 | }, 35 | { 36 | name: "100000", 37 | arr: []int{}, 38 | }, 39 | } 40 | 41 | func removeDropByTest(input interface{}) bool { 42 | return input.(int)%2 == 0 43 | } 44 | 45 | func init() { 46 | for j := 0; j < len(tDropByBenchs); j++ { 47 | length, _ := strconv.Atoi(tDropByBenchs[j].name) 48 | for i := 0; i < length/10; i++ { 49 | tDropByBenchs[j].arr = append(tDropByBenchs[j].arr.([]int), []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}...) 50 | } 51 | } 52 | } 53 | 54 | func TestDropBy(t *testing.T) { 55 | var tests = []TDropBy{ 56 | { 57 | name: "nil", 58 | arr: nil, 59 | want: nil, 60 | }, 61 | { 62 | name: "empty", 63 | arr: []int{}, 64 | want: []int{}, 65 | }, 66 | { 67 | name: "normal", 68 | arr: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 69 | want: []int{1, 3, 5, 7, 9}, 70 | }, 71 | } 72 | 73 | for _, subject := range tests { 74 | t.Run(subject.name, func(t *testing.T) { 75 | defer internal.DeferTestCases(t, subject.want) 76 | got := DropBy(subject.arr, removeDropByTest) 77 | 78 | if !generals.Same(got, subject.want) { 79 | t.Errorf("got = %v, wanted = %v", got, subject.want) 80 | return 81 | } 82 | }) 83 | } 84 | } 85 | 86 | func BenchmarkDropBy(b *testing.B) { 87 | for j := 0; j < len(tDropByBenchs); j++ { 88 | b.Run(fmt.Sprintf("slice_size_%s", tDropByBenchs[j].name), func(b *testing.B) { 89 | for i := 0; i < b.N; i++ { 90 | DropBy(tDropByBenchs[j].arr, removeDropByTest) 91 | } 92 | }) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /slices/drop_right.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Creates a sub slice from passed slice with n elements dropped from the end. 10 | // 11 | // Complexity: O(1) 12 | func DropRight(slice interface{}, n int) interface{} { 13 | if !internal.SliceCheck(slice) { 14 | panic("passed 'slice' variable is not slice type") 15 | } 16 | 17 | sliceValue := reflect.ValueOf(slice) 18 | if sliceValue.Len() < n { 19 | panic("'num' is bigger than slice length") 20 | } 21 | if n < 0 { 22 | panic("'num' is lower that zero") 23 | } 24 | 25 | return sliceValue.Slice(0, sliceValue.Len()-n).Interface() 26 | } 27 | -------------------------------------------------------------------------------- /slices/fill.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Fills a slice with a value from 'start' up to but not including 'end'. 10 | // 11 | // Complexity: O(n) 12 | // 13 | // n = end - start 14 | func Fill(slice, value interface{}, start, end int) interface{} { 15 | if !internal.SliceCheck(slice) { 16 | panic("passed 'slice' variable is not slice type") 17 | } 18 | 19 | sliceValue := reflect.ValueOf(slice) 20 | length := sliceValue.Len() 21 | outputValue := reflect.MakeSlice(reflect.TypeOf(slice), length, length) 22 | reflect.Copy(outputValue, sliceValue) 23 | 24 | if end < 0 { 25 | panic("negative values for 'end' variable is not accepted") 26 | } else if end > length { 27 | panic("'end' variable is bigger that slice length") 28 | } 29 | 30 | if start < 0 { 31 | panic("negative values for 'start' variable is not accepted") 32 | } else if start > end { 33 | panic("'start' variable is bigger than 'end' variable") 34 | } 35 | 36 | valueValue := reflect.ValueOf(value) 37 | for i := start; i < end; i++ { 38 | outputValue.Index(i).Set(valueValue) 39 | } 40 | 41 | return outputValue.Interface() 42 | } 43 | -------------------------------------------------------------------------------- /slices/find_index.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/generals" 7 | "github.com/golodash/godash/internal" 8 | ) 9 | 10 | // This method finds first index of the given slice which the given 11 | // value equals on that element's value. 12 | // 13 | // Complexity: O(n) 14 | func FindIndex(slice, value interface{}) int { 15 | if !internal.SliceCheck(slice) { 16 | panic("passed 'slice' variable is not slice type") 17 | } 18 | 19 | sliceValue := reflect.ValueOf(slice) 20 | for i := 0; i < sliceValue.Len(); i++ { 21 | if generals.Same(reflect.ValueOf(sliceValue.Index(i).Interface()).Interface(), value) { 22 | return i 23 | } 24 | } 25 | 26 | return -1 27 | } 28 | -------------------------------------------------------------------------------- /slices/find_index_by.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // This method finds first index of the given slice which the given 10 | // function on that element, returns true. 11 | // 12 | // example for function: 13 | // 14 | // func isEqual(value interface{}) bool { 15 | // return value.(int) == 5 16 | // } 17 | // 18 | // Complexity: O(n) 19 | func FindIndexBy(slice interface{}, function func(interface{}) bool) int { 20 | if !internal.SliceCheck(slice) { 21 | panic("passed 'slice' variable is not slice type") 22 | } 23 | 24 | sliceValue := reflect.ValueOf(slice) 25 | for i := 0; i < sliceValue.Len(); i++ { 26 | if function(reflect.ValueOf(sliceValue.Index(i).Interface()).Interface()) { 27 | return i 28 | } 29 | } 30 | 31 | return -1 32 | } 33 | -------------------------------------------------------------------------------- /slices/find_index_by_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TFindIndexBy struct { 13 | name string 14 | arr interface{} 15 | want interface{} 16 | } 17 | 18 | var tFindIndexByBenchs = []TFindIndexBy{ 19 | { 20 | name: "10", 21 | arr: []int{}, 22 | }, 23 | { 24 | name: "100", 25 | arr: []int{}, 26 | }, 27 | { 28 | name: "1000", 29 | arr: []int{}, 30 | }, 31 | { 32 | name: "10000", 33 | arr: []int{}, 34 | }, 35 | { 36 | name: "100000", 37 | arr: []int{}, 38 | }, 39 | } 40 | 41 | func init() { 42 | for j := 0; j < len(tFindIndexByBenchs); j++ { 43 | length, _ := strconv.Atoi(tFindIndexByBenchs[j].name) 44 | for i := 0; i < length/10; i++ { 45 | tFindIndexByBenchs[j].arr = append(tFindIndexByBenchs[j].arr.([]int), []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}...) 46 | } 47 | } 48 | } 49 | 50 | func compareFindIndexByTest(value interface{}) bool { 51 | return value.(int) == 5 52 | } 53 | 54 | func TestFindIndexBy(t *testing.T) { 55 | var tests = []TFindIndexBy{ 56 | { 57 | name: "nil", 58 | arr: nil, 59 | want: nil, 60 | }, 61 | { 62 | name: "empty", 63 | arr: []int{}, 64 | want: -1, 65 | }, 66 | { 67 | name: "normal", 68 | arr: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 69 | want: 5, 70 | }, 71 | { 72 | name: "can't be found", 73 | arr: []int{0, 1, 2, 3, 4, 6, 7, 8, 9}, 74 | want: -1, 75 | }, 76 | } 77 | 78 | for _, subject := range tests { 79 | t.Run(subject.name, func(t *testing.T) { 80 | defer internal.DeferTestCases(t, subject.want) 81 | got := FindIndexBy(subject.arr, compareFindIndexByTest) 82 | 83 | if !generals.Same(got, subject.want) { 84 | t.Errorf("got = %v, wanted = %v", got, subject.want) 85 | return 86 | } 87 | }) 88 | } 89 | } 90 | 91 | func BenchmarkFindIndexBy(b *testing.B) { 92 | for j := 0; j < len(tFindIndexByBenchs); j++ { 93 | b.Run(fmt.Sprintf("slice_size_%s", tFindIndexByBenchs[j].name), func(b *testing.B) { 94 | for i := 0; i < b.N; i++ { 95 | FindIndexBy(tFindIndexByBenchs[j].arr, compareFindIndexByTest) 96 | } 97 | }) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /slices/find_index_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TFindIndex struct { 13 | name string 14 | arr interface{} 15 | value interface{} 16 | want interface{} 17 | } 18 | 19 | var tFindIndexBenchs = []TFindIndex{ 20 | { 21 | name: "10", 22 | arr: []int{}, 23 | value: 100, 24 | }, 25 | { 26 | name: "100", 27 | arr: []int{}, 28 | value: 100, 29 | }, 30 | { 31 | name: "1000", 32 | arr: []int{}, 33 | value: 100, 34 | }, 35 | { 36 | name: "10000", 37 | arr: []int{}, 38 | value: 100, 39 | }, 40 | { 41 | name: "100000", 42 | arr: []int{}, 43 | value: 100, 44 | }, 45 | } 46 | 47 | func init() { 48 | for j := 0; j < len(tFindIndexBenchs); j++ { 49 | length, _ := strconv.Atoi(tFindIndexBenchs[j].name) 50 | for i := 0; i < length/10; i++ { 51 | tFindIndexBenchs[j].arr = append(tFindIndexBenchs[j].arr.([]int), []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}...) 52 | } 53 | } 54 | } 55 | 56 | func TestFindIndex(t *testing.T) { 57 | var tests = []TFindIndex{ 58 | { 59 | name: "nil", 60 | arr: nil, 61 | value: 100, 62 | want: nil, 63 | }, 64 | { 65 | name: "empty", 66 | arr: []int{}, 67 | value: 100, 68 | want: -1, 69 | }, 70 | { 71 | name: "normal", 72 | arr: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 73 | value: 5, 74 | want: 5, 75 | }, 76 | { 77 | name: "can't be found", 78 | arr: []int{0, 1, 2, 3, 4, 6, 7, 8, 9}, 79 | value: 10, 80 | want: -1, 81 | }, 82 | } 83 | 84 | for _, subject := range tests { 85 | t.Run(subject.name, func(t *testing.T) { 86 | defer internal.DeferTestCases(t, subject.want) 87 | got := FindIndex(subject.arr, subject.value) 88 | 89 | if !generals.Same(got, subject.want) { 90 | t.Errorf("got = %v, wanted = %v", got, subject.want) 91 | return 92 | } 93 | }) 94 | } 95 | } 96 | 97 | func BenchmarkFindIndex(b *testing.B) { 98 | for j := 0; j < len(tFindIndexBenchs); j++ { 99 | b.Run(fmt.Sprintf("slice_size_%s", tFindIndexBenchs[j].name), func(b *testing.B) { 100 | for i := 0; i < b.N; i++ { 101 | FindIndex(tFindIndexBenchs[j].arr, tFindIndexBenchs[j].value) 102 | } 103 | }) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /slices/first.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | // Gets the first element of slice. 4 | // 5 | // Complexity: O(1) 6 | func First(slice interface{}) interface{} { 7 | return Head(slice) 8 | } 9 | -------------------------------------------------------------------------------- /slices/flatten.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Flattens slice a single level deep. 10 | // 11 | // Complexity: O(n) 12 | func Flatten(slice interface{}) interface{} { 13 | if !internal.SliceCheck(slice) { 14 | panic("passed 'slice' variable is not slice type") 15 | } 16 | 17 | sliceValue := reflect.ValueOf(slice) 18 | var output reflect.Value 19 | if sliceValue.Type().Elem().Kind() == reflect.Interface { 20 | output = reflect.MakeSlice(reflect.TypeOf([]interface{}{}), 0, sliceValue.Len()) 21 | } else if sliceValue.Type().Elem().Kind() == reflect.Slice { 22 | output = reflect.MakeSlice(sliceValue.Type().Elem(), 0, sliceValue.Len()) 23 | } else { 24 | return slice 25 | } 26 | 27 | for i := 0; i < sliceValue.Len(); i++ { 28 | item := reflect.ValueOf(sliceValue.Index(i).Interface()) 29 | if item.Kind() == reflect.Slice { 30 | for j := 0; j < item.Len(); j++ { 31 | output = reflect.Append(output, item.Index(j)) 32 | } 33 | } else { 34 | output = reflect.Append(output, item) 35 | } 36 | } 37 | 38 | return output.Interface() 39 | } 40 | -------------------------------------------------------------------------------- /slices/flatten_deep.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Recursively flattens slice. 10 | // 11 | // Complexity: O(n) 12 | // 13 | // n = count of all non-slice type elements of 'slice' 14 | func FlattenDeep(slice interface{}) interface{} { 15 | if !internal.SliceCheck(slice) { 16 | panic("passed 'slice' variable is not slice type") 17 | } 18 | 19 | sliceItemType := reflect.TypeOf(slice) 20 | for sliceItemType.Kind() == reflect.Slice { 21 | sliceItemType = sliceItemType.Elem() 22 | } 23 | 24 | return recursiveFlattenDeep(slice, sliceItemType).Interface() 25 | } 26 | 27 | func recursiveFlattenDeep(slice interface{}, itemType reflect.Type) reflect.Value { 28 | s := reflect.MakeSlice(reflect.SliceOf(itemType), 0, 0) 29 | sliceValue := reflect.ValueOf(slice) 30 | for i := 0; i < sliceValue.Len(); i++ { 31 | item := reflect.ValueOf(sliceValue.Index(i).Interface()) 32 | if item.Kind() == reflect.Slice { 33 | s = reflect.AppendSlice(s, recursiveFlattenDeep(item.Interface(), itemType)) 34 | } else { 35 | s = reflect.Append(s, item) 36 | } 37 | } 38 | 39 | return s 40 | } 41 | -------------------------------------------------------------------------------- /slices/flatten_deep_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TFlattenDeep struct { 13 | name string 14 | arr interface{} 15 | want interface{} 16 | } 17 | 18 | var tFlattenDeepBenchs = []TFlattenDeep{ 19 | { 20 | name: "10", 21 | arr: []interface{}{}, 22 | }, 23 | { 24 | name: "100", 25 | arr: []interface{}{}, 26 | }, 27 | { 28 | name: "1000", 29 | arr: []interface{}{}, 30 | }, 31 | { 32 | name: "10000", 33 | arr: []interface{}{}, 34 | }, 35 | { 36 | name: "100000", 37 | arr: []interface{}{}, 38 | }, 39 | } 40 | 41 | func init() { 42 | for j := 0; j < len(tFlattenDeepBenchs); j++ { 43 | length, _ := strconv.Atoi(tFlattenDeepBenchs[j].name) 44 | for i := 0; i < length/10; i++ { 45 | tFlattenDeepBenchs[j].arr = append(tFlattenDeepBenchs[j].arr.([]interface{}), [][][]interface{}{{{0, 1, 2}}, {{3, 4, 5, 6, 7, 8, 9}}}) 46 | } 47 | } 48 | } 49 | 50 | func TestFlattenDeep(t *testing.T) { 51 | var tests = []TFlattenDeep{ 52 | { 53 | name: "nil", 54 | arr: nil, 55 | want: nil, 56 | }, 57 | { 58 | name: "empty", 59 | arr: []interface{}{}, 60 | want: []interface{}{}, 61 | }, 62 | { 63 | name: "none", 64 | arr: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 65 | want: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 66 | }, 67 | { 68 | name: "normal", 69 | arr: []interface{}{0, []interface{}{1, 2}, [][]interface{}{{3}, {4, 5}}, []interface{}{6, 7}, 8, 9}, 70 | want: []interface{}{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 71 | }, 72 | { 73 | name: "more layer", 74 | arr: [][][]int{{{0, 1, 2}}, {{3}, {4, 5, 6}}, {{}}, {{7}}, {{8, 9}}}, 75 | want: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 76 | }, 77 | } 78 | 79 | for _, subject := range tests { 80 | t.Run(subject.name, func(t *testing.T) { 81 | defer internal.DeferTestCases(t, subject.want) 82 | got := FlattenDeep(subject.arr) 83 | 84 | if !generals.Same(got, subject.want) { 85 | t.Errorf("got = %v, wanted = %v", got, subject.want) 86 | return 87 | } 88 | }) 89 | } 90 | } 91 | 92 | func BenchmarkFlattenDeep(b *testing.B) { 93 | for j := 0; j < len(tFlattenDeepBenchs); j++ { 94 | b.Run(fmt.Sprintf("slice_size_%s", tFlattenDeepBenchs[j].name), func(b *testing.B) { 95 | for i := 0; i < b.N; i++ { 96 | FlattenDeep(tFlattenDeepBenchs[j].arr) 97 | } 98 | }) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /slices/flatten_depth.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Flattens slice 'depth' level deep. 10 | // 11 | // Complexity: O(n) 12 | // 13 | // n = count of all elements at 'depth' level of 'slice' 14 | func FlattenDepth(slice interface{}, depth int) interface{} { 15 | if !internal.SliceCheck(slice) { 16 | panic("passed 'slice' variable is not slice type") 17 | } 18 | 19 | if depth == 0 { 20 | return slice 21 | } else if depth < 0 { 22 | return negativeFlatten(slice, -depth) 23 | } else { 24 | return recursiveFlattenDepth(slice, depth, getTypeInGivenDepth(slice, depth)).Interface() 25 | } 26 | } 27 | 28 | func getTypeInGivenDepth(slice interface{}, depth int) reflect.Type { 29 | sliceItemType := reflect.TypeOf(slice) 30 | for sliceItemType.Kind() == reflect.Slice { 31 | if depth == 0 { 32 | break 33 | } 34 | sliceItemType = sliceItemType.Elem() 35 | depth-- 36 | } 37 | 38 | return sliceItemType 39 | } 40 | 41 | func negativeFlatten(slice interface{}, depth int) interface{} { 42 | sliceValue := reflect.ValueOf(slice) 43 | if depth > 0 { 44 | returnedSlice := negativeFlatten(slice, depth-1) 45 | sliceValue = reflect.Append(reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(returnedSlice)), 0, 1), reflect.ValueOf(returnedSlice)) 46 | } 47 | 48 | return sliceValue.Interface() 49 | } 50 | 51 | func recursiveFlattenDepth(slice interface{}, depth int, itemType reflect.Type) reflect.Value { 52 | s := reflect.MakeSlice(reflect.SliceOf(itemType), 0, 0) 53 | sliceValue := reflect.ValueOf(slice) 54 | for i := 0; i < sliceValue.Len(); i++ { 55 | item := reflect.ValueOf(sliceValue.Index(i).Interface()) 56 | if item.Kind() == reflect.Slice && depth != 0 { 57 | s = reflect.AppendSlice(s, recursiveFlattenDepth(item.Interface(), depth-1, itemType)) 58 | } else { 59 | s = reflect.Append(s, item) 60 | } 61 | } 62 | 63 | return s 64 | } 65 | -------------------------------------------------------------------------------- /slices/from_pairs.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | 7 | "github.com/golodash/godash/internal" 8 | ) 9 | 10 | // This method returns an object composed from key-value pairs. 11 | // 12 | // Complexity: O(n) 13 | func FromPairs(slice interface{}) interface{} { 14 | if !internal.SliceCheck(slice) { 15 | panic("passed 'slice' variable is not slice type") 16 | } 17 | 18 | sliceItemType := reflect.TypeOf(slice) 19 | if sliceItemType = sliceItemType.Elem(); sliceItemType.Kind() == reflect.Slice { 20 | if sliceItemType = sliceItemType.Elem(); sliceItemType.Kind() == reflect.Slice { 21 | _ = 0 22 | } 23 | } 24 | 25 | sliceValue := reflect.ValueOf(slice) 26 | output := reflect.MakeMap(reflect.MapOf(sliceItemType, sliceItemType)) 27 | for i := 0; i < sliceValue.Len(); i++ { 28 | item := reflect.ValueOf(sliceValue.Index(i).Interface()) 29 | if !internal.SliceCheck(item.Interface()) { 30 | panic(fmt.Sprintf("item in index %d is not even a slice", i)) 31 | } 32 | 33 | if item.Len() == 2 { 34 | if key, ok := item.Index(0).Interface().(string); ok { 35 | output.SetMapIndex(reflect.ValueOf(key), item.Index(1)) 36 | } 37 | } else if item.Len() == 1 { 38 | if key, ok := item.Index(0).Interface().(string); ok { 39 | output.SetMapIndex(reflect.ValueOf(key), reflect.Zero(sliceItemType)) 40 | } 41 | } 42 | } 43 | 44 | return output.Interface() 45 | } 46 | -------------------------------------------------------------------------------- /slices/head.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Gets the first element of slice. 10 | // 11 | // Complexity: O(1) 12 | func Head(slice interface{}) interface{} { 13 | if !internal.SliceCheck(slice) { 14 | panic("passed 'slice' variable is not slice type") 15 | } 16 | 17 | sliceValue := reflect.ValueOf(slice) 18 | if sliceValue.Len() == 0 { 19 | panic("slice is empty") 20 | } 21 | 22 | return sliceValue.Index(0).Interface() 23 | } 24 | -------------------------------------------------------------------------------- /slices/head_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type THead struct { 13 | name string 14 | arr interface{} 15 | want interface{} 16 | } 17 | 18 | var tHeadBenchs = []THead{ 19 | { 20 | name: "10", 21 | arr: []interface{}{}, 22 | }, 23 | { 24 | name: "100", 25 | arr: []interface{}{}, 26 | }, 27 | { 28 | name: "1000", 29 | arr: []interface{}{}, 30 | }, 31 | { 32 | name: "10000", 33 | arr: []interface{}{}, 34 | }, 35 | { 36 | name: "100000", 37 | arr: []interface{}{}, 38 | }, 39 | } 40 | 41 | func init() { 42 | for j := 0; j < len(tHeadBenchs); j++ { 43 | length, _ := strconv.Atoi(tHeadBenchs[j].name) 44 | for i := 0; i < length/10; i++ { 45 | tHeadBenchs[j].arr = append(tHeadBenchs[j].arr.([]interface{}), []interface{}{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}...) 46 | } 47 | } 48 | } 49 | 50 | func TestHead(t *testing.T) { 51 | tests := []THead{ 52 | { 53 | name: "nil", 54 | arr: nil, 55 | want: nil, 56 | }, 57 | { 58 | name: "empty", 59 | arr: []interface{}{}, 60 | want: nil, 61 | }, 62 | { 63 | name: "normal", 64 | arr: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 65 | want: 0, 66 | }, 67 | } 68 | 69 | for _, subject := range tests { 70 | t.Run(subject.name, func(t *testing.T) { 71 | defer internal.DeferTestCases(t, subject.want) 72 | got := Head(subject.arr) 73 | 74 | if !generals.Same(got, subject.want) { 75 | t.Errorf("got = %v, wanted = %v", got, subject.want) 76 | return 77 | } 78 | }) 79 | } 80 | } 81 | 82 | func BenchmarkHead(b *testing.B) { 83 | for j := 0; j < len(tHeadBenchs); j++ { 84 | b.Run(fmt.Sprintf("slice_size_%s", tHeadBenchs[j].name), func(b *testing.B) { 85 | for i := 0; i < b.N; i++ { 86 | Head(tHeadBenchs[j].arr) 87 | } 88 | }) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /slices/index_of.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/generals" 7 | "github.com/golodash/godash/internal" 8 | ) 9 | 10 | // Gets the index at which the first occurrence of value is found in slice 11 | // with equality comparisons. If 'from' is negative, it's used as the offset 12 | // from the end of slice. 13 | // 14 | // Complexity: O(n) 15 | func IndexOf(slice, value interface{}, from int) int { 16 | return indexOf(slice, value, from, true) 17 | } 18 | 19 | func indexOf(slice, value interface{}, from int, ltr bool) int { 20 | if !internal.SliceCheck(slice) { 21 | panic("passed 'slice' variable is not slice type") 22 | } 23 | 24 | sliceValue := reflect.ValueOf(slice) 25 | if sliceValue.Len() == 0 { 26 | return -1 27 | } 28 | 29 | if from < 0 { 30 | from = sliceValue.Len() + from 31 | } else if from >= sliceValue.Len() { 32 | panic("'from' index is out of range") 33 | } 34 | 35 | var until int 36 | var count int 37 | if ltr { 38 | until = sliceValue.Len() 39 | count = +1 40 | } else { 41 | until = -1 42 | count = -1 43 | if sliceValue.Len() == 0 { 44 | until = 0 45 | } 46 | } 47 | 48 | compare := func(i, until int) bool { 49 | if until == -1 { 50 | return i > until 51 | } else { 52 | return i < until 53 | } 54 | } 55 | 56 | for i := from; compare(i, until); i += count { 57 | if !generals.Same(sliceValue.Index(i).Interface(), value) { 58 | continue 59 | } else { 60 | return i 61 | } 62 | } 63 | 64 | return -1 65 | } 66 | -------------------------------------------------------------------------------- /slices/initial.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Gets all but the last element of slice. 10 | // 11 | // Complexity: O(1) 12 | func Initial(slice interface{}) interface{} { 13 | if !internal.SliceCheck(slice) { 14 | panic("passed 'slice' variable is not slice type") 15 | } 16 | 17 | sliceValue := reflect.ValueOf(slice) 18 | if sliceValue.Len() == 0 { 19 | return slice 20 | } 21 | 22 | return sliceValue.Slice(0, sliceValue.Len()-1).Interface() 23 | } 24 | -------------------------------------------------------------------------------- /slices/initial_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TInitial struct { 13 | name string 14 | arr interface{} 15 | want interface{} 16 | } 17 | 18 | var tInitialBenchs = []TInitial{ 19 | { 20 | name: "10", 21 | arr: []interface{}{}, 22 | }, 23 | { 24 | name: "100", 25 | arr: []interface{}{}, 26 | }, 27 | { 28 | name: "1000", 29 | arr: []interface{}{}, 30 | }, 31 | { 32 | name: "10000", 33 | arr: []interface{}{}, 34 | }, 35 | { 36 | name: "100000", 37 | arr: []interface{}{}, 38 | }, 39 | } 40 | 41 | func init() { 42 | for j := 0; j < len(tInitialBenchs); j++ { 43 | length, _ := strconv.Atoi(tInitialBenchs[j].name) 44 | for i := 0; i < length/10; i++ { 45 | tInitialBenchs[j].arr = append(tInitialBenchs[j].arr.([]interface{}), []interface{}{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}...) 46 | } 47 | } 48 | } 49 | 50 | func TestInitial(t *testing.T) { 51 | var tests = []TInitial{ 52 | { 53 | name: "nil", 54 | arr: nil, 55 | want: nil, 56 | }, 57 | { 58 | name: "empty", 59 | arr: []interface{}{}, 60 | want: []interface{}{}, 61 | }, 62 | { 63 | name: "just one item", 64 | arr: []interface{}{0}, 65 | want: []interface{}{}, 66 | }, 67 | { 68 | name: "normal", 69 | arr: []interface{}{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 70 | want: []interface{}{0, 1, 2, 3, 4, 5, 6, 7, 8}, 71 | }, 72 | { 73 | name: "more complex", 74 | arr: []interface{}{0, []interface{}{1, 2}, []interface{}{3, 4, 5}, []interface{}{6, 7}, 8, []interface{}{9}}, 75 | want: []interface{}{0, []interface{}{1, 2}, []interface{}{3, 4, 5}, []interface{}{6, 7}, 8}, 76 | }, 77 | { 78 | name: "type based", 79 | arr: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 80 | want: []int{0, 1, 2, 3, 4, 5, 6, 7, 8}, 81 | }, 82 | } 83 | 84 | for _, subject := range tests { 85 | t.Run(subject.name, func(t *testing.T) { 86 | defer internal.DeferTestCases(t, subject.want) 87 | got := Initial(subject.arr) 88 | 89 | if !generals.Same(got, subject.want) { 90 | t.Errorf("got = %v, wanted = %v", got, subject.want) 91 | return 92 | } 93 | }) 94 | } 95 | } 96 | 97 | func BenchmarkInitial(b *testing.B) { 98 | for j := 0; j < len(tInitialBenchs); j++ { 99 | b.Run(fmt.Sprintf("slice_size_%s", tInitialBenchs[j].name), func(b *testing.B) { 100 | for i := 0; i < b.N; i++ { 101 | Initial(tInitialBenchs[j].arr) 102 | } 103 | }) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /slices/intersection.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Creates a slice of unique values that are included in all given slices 10 | // for equality comparisons. The order and references of result values are 11 | // determined by the first slice. 12 | // 13 | // Complexity: O(n) 14 | // 15 | // n = number of all elements in slices of 'slices' 16 | func Intersection(slices interface{}) interface{} { 17 | if !internal.SliceCheck(slices) { 18 | panic("passed 'slices' variable is not slice type") 19 | } 20 | 21 | sliceItemType := reflect.TypeOf(slices) 22 | if sliceItemType = sliceItemType.Elem(); sliceItemType.Kind() == reflect.Slice { 23 | sliceItemType = sliceItemType.Elem() 24 | } 25 | 26 | sliceValue := reflect.ValueOf(slices) 27 | length := 0 28 | for i := 0; i < sliceValue.Len(); i++ { 29 | subSlice := reflect.ValueOf(sliceValue.Index(i).Interface()) 30 | if !internal.SliceCheck(subSlice.Interface()) { 31 | continue 32 | } 33 | 34 | length += subSlice.Len() 35 | } 36 | 37 | seenMap := reflect.MakeMap(reflect.MapOf(sliceItemType, reflect.TypeOf(false))) 38 | outputSlice := reflect.MakeSlice(reflect.SliceOf(sliceItemType), 0, length) 39 | for i := 0; i < sliceValue.Len(); i++ { 40 | subSlice := reflect.ValueOf(sliceValue.Index(i).Interface()) 41 | if !internal.SliceCheck(subSlice.Interface()) { 42 | continue 43 | } 44 | 45 | for j := 0; j < subSlice.Len(); j++ { 46 | item := reflect.ValueOf(subSlice.Index(j).Interface()) 47 | var value reflect.Value = reflect.Value{} 48 | if value = seenMap.MapIndex(item); value.IsValid() && !value.IsZero() { 49 | continue 50 | } 51 | 52 | outputSlice = reflect.Append(outputSlice, item) 53 | seenMap.SetMapIndex(item, reflect.ValueOf(true)) 54 | } 55 | } 56 | 57 | return outputSlice.Interface() 58 | } 59 | -------------------------------------------------------------------------------- /slices/intersection_by.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // This method is like Intersection except that it accepts comparator which is 10 | // invoked to compare elements of 'slices'. The order and references of result 11 | // values are determined by the first slice. The comparator is invoked with 12 | // two arguments: (slice1, slice2). 13 | // 14 | // example for function: 15 | // 16 | // func isEqual(value1, value2 interface{}) bool { 17 | // v1 := value1.([]int) 18 | // v2 := value2.([]int) 19 | // same := true 20 | // for i := 0; i < v1.len(); i ++ { 21 | // if v1[i] != v2[i] { 22 | // same = false 23 | // break 24 | // } 25 | // } 26 | // return same 27 | // } 28 | // 29 | // Complexity: O(n*log(n)) 30 | func IntersectionBy(slices interface{}, function func(interface{}, interface{}) bool) interface{} { 31 | if !internal.SliceCheck(slices) { 32 | panic("passed 'slices' variable is not slice type") 33 | } 34 | 35 | sliceItemType := reflect.TypeOf(slices) 36 | if sliceItemType = sliceItemType.Elem(); sliceItemType.Kind() == reflect.Slice { 37 | sliceItemType = sliceItemType.Elem() 38 | } 39 | 40 | sliceValue := reflect.ValueOf(slices) 41 | length := 0 42 | for i := 0; i < sliceValue.Len(); i++ { 43 | subSlice := reflect.ValueOf(sliceValue.Index(i).Interface()) 44 | if !internal.SliceCheck(subSlice.Interface()) { 45 | continue 46 | } 47 | 48 | length += subSlice.Len() 49 | } 50 | 51 | outputSlice := reflect.MakeSlice(reflect.SliceOf(sliceItemType), 0, length) 52 | for i := 0; i < sliceValue.Len(); i++ { 53 | subSlice := reflect.ValueOf(sliceValue.Index(i).Interface()) 54 | if !internal.SliceCheck(subSlice.Interface()) { 55 | continue 56 | } 57 | 58 | for j := i + 1; j < sliceValue.Len(); j++ { 59 | secondSubSlice := reflect.ValueOf(sliceValue.Index(i + 1).Interface()) 60 | if function(subSlice.Interface(), secondSubSlice.Interface()) { 61 | outputSlice = reflect.AppendSlice(outputSlice, subSlice) 62 | break 63 | } 64 | } 65 | } 66 | 67 | return outputSlice.Interface() 68 | } 69 | -------------------------------------------------------------------------------- /slices/join.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | 7 | "github.com/golodash/godash/internal" 8 | ) 9 | 10 | // Converts all elements in slice into a string separated by 'separator'. 11 | // 12 | // Complexity: O(n) 13 | func Join(slice interface{}, separator string) string { 14 | if !internal.SliceCheck(slice) { 15 | panic("passed 'slice' variable is not slice type") 16 | } 17 | 18 | sliceValue := reflect.ValueOf(slice) 19 | var result string 20 | for i := 0; i < sliceValue.Len(); i++ { 21 | if len(result) == 0 { 22 | result = toString(sliceValue.Index(0).Interface()) 23 | } else { 24 | result += separator + toString(sliceValue.Index(i).Interface()) 25 | } 26 | } 27 | return result 28 | } 29 | 30 | func toString(input interface{}) string { 31 | inputValue := reflect.ValueOf(input) 32 | if !inputValue.IsValid() { 33 | return "" 34 | } 35 | 36 | return fmt.Sprint(input) 37 | } 38 | -------------------------------------------------------------------------------- /slices/join_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TJoin struct { 13 | name string 14 | arg1 interface{} 15 | arg2 string 16 | expected interface{} 17 | } 18 | 19 | var TJoinBenchs = []TJoin{ 20 | { 21 | name: "10", 22 | arg1: []string{}, 23 | }, 24 | { 25 | name: "100", 26 | arg1: []string{}, 27 | }, 28 | { 29 | name: "1000", 30 | arg1: []string{}, 31 | }, 32 | { 33 | name: "10000", 34 | arg1: []string{}, 35 | }, 36 | { 37 | name: "100000", 38 | arg1: []string{}, 39 | }, 40 | } 41 | 42 | func init() { 43 | for i := 0; i < len(TJoinBenchs); i++ { 44 | k, _ := strconv.Atoi(TJoinBenchs[i].name) 45 | for j := 0; j < k/10; j++ { 46 | TJoinBenchs[i].arg1 = append(TJoinBenchs[i].arg1.([]string), []string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J"}...) 47 | } 48 | 49 | } 50 | } 51 | 52 | func TestJoin(t *testing.T) { 53 | var tests = []TJoin{ 54 | { 55 | name: "nil", 56 | arg1: nil, 57 | arg2: "-", 58 | expected: nil, 59 | }, 60 | { 61 | name: "empty", 62 | arg1: []string{}, 63 | arg2: "<", 64 | expected: "", 65 | }, 66 | { 67 | name: "normal", 68 | arg1: []string{"A", "B", "C", "D"}, 69 | arg2: "-", 70 | expected: "A-B-C-D", 71 | }, 72 | { 73 | name: "type based", 74 | arg1: []int{1, 2, 3, 4, 5, 6}, 75 | arg2: ", ", 76 | expected: "1, 2, 3, 4, 5, 6", 77 | }, 78 | } 79 | for _, subject := range tests { 80 | t.Run(subject.name, func(t *testing.T) { 81 | defer internal.DeferTestCases(t, subject.expected) 82 | got := Join(subject.arg1, subject.arg2) 83 | 84 | if !generals.Same(got, subject.expected) { 85 | t.Errorf("got = %v, wanted = %v", got, subject.expected) 86 | return 87 | } 88 | }) 89 | } 90 | } 91 | 92 | func BenchmarkJoin(b *testing.B) { 93 | for _, sample := range TJoinBenchs { 94 | b.Run(fmt.Sprintf("input_size_%s", sample.name), func(b *testing.B) { 95 | for i := 0; i < b.N; i++ { 96 | Join(sample.arg1, sample.arg2) 97 | } 98 | }) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /slices/last_index_of.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | // This method is like IndexOf except that it iterates over elements of 4 | // 'slice' from right to left. If 'from' is negative, it's used as the offset 5 | // from the end of slice. 6 | // 7 | // Complexity: O(n) 8 | func LastIndexOf(slice, value interface{}, from int) int { 9 | return indexOf(slice, value, from, false) 10 | } 11 | -------------------------------------------------------------------------------- /slices/last_index_of_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TLastIndexOf struct { 13 | name string 14 | arr interface{} 15 | value interface{} 16 | want interface{} 17 | } 18 | 19 | var tLastIndexOfBenchs = []TLastIndexOf{ 20 | { 21 | name: "10", 22 | arr: []int{}, 23 | value: 10, 24 | }, 25 | { 26 | name: "100", 27 | arr: []int{}, 28 | value: 10, 29 | }, 30 | { 31 | name: "1000", 32 | arr: []int{}, 33 | value: 10, 34 | }, 35 | { 36 | name: "10000", 37 | arr: []int{}, 38 | value: 10, 39 | }, 40 | { 41 | name: "100000", 42 | arr: []int{}, 43 | value: 10, 44 | }, 45 | } 46 | 47 | func init() { 48 | for j := 0; j < len(tLastIndexOfBenchs); j++ { 49 | length, _ := strconv.Atoi(tLastIndexOfBenchs[j].name) 50 | for i := 0; i < length/10; i++ { 51 | tLastIndexOfBenchs[j].arr = append(tLastIndexOfBenchs[j].arr.([]int), []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}...) 52 | } 53 | tLastIndexOfBenchs[j].arr = append(tLastIndexOfBenchs[j].arr.([]int), 10) 54 | } 55 | } 56 | 57 | func TestLastIndexOf(t *testing.T) { 58 | var tests = []TLastIndexOf{ 59 | { 60 | name: "nil", 61 | arr: nil, 62 | value: -1, 63 | want: nil, 64 | }, 65 | { 66 | name: "empty", 67 | arr: []int{}, 68 | value: -1, 69 | want: -1, 70 | }, 71 | { 72 | name: "normal", 73 | arr: []int{0, 1, 2, 3, 4, 5, 4, 7, 8, 9}, 74 | value: 4, 75 | want: 6, 76 | }, 77 | { 78 | name: "does not exist", 79 | arr: []int{0, 1, 2, 3, 4, 6, 7, 8, 9}, 80 | value: 10, 81 | want: -1, 82 | }, 83 | } 84 | 85 | for _, subject := range tests { 86 | t.Run(subject.name, func(t *testing.T) { 87 | defer internal.DeferTestCases(t, subject.want) 88 | got := LastIndexOf(subject.arr, subject.value, -1) 89 | 90 | if !generals.Same(got, subject.want) { 91 | t.Errorf("got = %v, wanted = %v", got, subject.want) 92 | return 93 | } 94 | }) 95 | } 96 | } 97 | 98 | func BenchmarkLastIndexOf(b *testing.B) { 99 | for j := 0; j < len(tLastIndexOfBenchs); j++ { 100 | b.Run(fmt.Sprintf("slice_size_%s", tLastIndexOfBenchs[j].name), func(b *testing.B) { 101 | for i := 0; i < b.N; i++ { 102 | LastIndexOf(tLastIndexOfBenchs[j].arr, tLastIndexOfBenchs[j].value, -1) 103 | } 104 | }) 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /slices/latest_last.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Gets the last element of slice. 10 | // 11 | // Complexity: O(1) 12 | func Latest(slice interface{}) interface{} { 13 | if !internal.SliceCheck(slice) { 14 | panic("passed 'slice' variable is not slice type") 15 | } 16 | 17 | s := reflect.ValueOf(slice) 18 | if s.Len() == 0 { 19 | panic("slice is empty") 20 | } 21 | 22 | return s.Index(s.Len() - 1).Interface() 23 | } 24 | 25 | // Gets the last element of slice. 26 | // 27 | // Complexity: O(1) 28 | func Last(slice interface{}) interface{} { 29 | return Latest(slice) 30 | } 31 | -------------------------------------------------------------------------------- /slices/latest_last_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TLatest struct { 13 | name string 14 | arr interface{} 15 | want interface{} 16 | } 17 | 18 | var tLatestBenchs = []TLatest{ 19 | { 20 | name: "10", 21 | arr: []interface{}{}, 22 | }, 23 | { 24 | name: "100", 25 | arr: []interface{}{}, 26 | }, 27 | { 28 | name: "1000", 29 | arr: []interface{}{}, 30 | }, 31 | { 32 | name: "10000", 33 | arr: []interface{}{}, 34 | }, 35 | { 36 | name: "100000", 37 | arr: []interface{}{}, 38 | }, 39 | } 40 | 41 | func init() { 42 | for j := 0; j < len(tLatestBenchs); j++ { 43 | length, _ := strconv.Atoi(tLatestBenchs[j].name) 44 | for i := 0; i < length/10; i++ { 45 | tLatestBenchs[j].arr = append(tLatestBenchs[j].arr.([]interface{}), []interface{}{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}...) 46 | } 47 | } 48 | } 49 | 50 | func TestLatest(t *testing.T) { 51 | tests := []TLatest{ 52 | { 53 | name: "nil", 54 | arr: nil, 55 | want: nil, 56 | }, 57 | { 58 | name: "empty", 59 | arr: []interface{}{}, 60 | want: nil, 61 | }, 62 | { 63 | name: "normal", 64 | arr: []interface{}{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 65 | want: 9, 66 | }, 67 | } 68 | 69 | for _, subject := range tests { 70 | t.Run(subject.name, func(t *testing.T) { 71 | defer internal.DeferTestCases(t, subject.want) 72 | got := Latest(subject.arr) 73 | 74 | if !generals.Same(got, subject.want) { 75 | t.Errorf("got = %v, wanted = %v", got, subject.want) 76 | return 77 | } 78 | }) 79 | } 80 | } 81 | 82 | func BenchmarkLatest(b *testing.B) { 83 | for j := 0; j < len(tLatestBenchs); j++ { 84 | b.Run(fmt.Sprintf("slice_size_%s", tLatestBenchs[j].name), func(b *testing.B) { 85 | for i := 0; i < b.N; i++ { 86 | Latest(tLatestBenchs[j].arr) 87 | } 88 | }) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /slices/max.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Computes the maximum value of slice. 10 | // 11 | // Complexity: O(n) 12 | func Max(slice interface{}) interface{} { 13 | if !internal.SliceCheck(slice) { 14 | panic("'slice' is not slice type") 15 | } 16 | 17 | sliceValue := reflect.ValueOf(slice) 18 | 19 | if sliceValue.Len() == 0 { 20 | return nil 21 | } 22 | 23 | biggest := sliceValue.Index(0) 24 | for i := 0; i < sliceValue.Len(); i++ { 25 | element := sliceValue.Index(i) 26 | if res := internal.CompareNumbers(element.Interface(), biggest.Interface()); res == internal.Higher { 27 | biggest = element 28 | } 29 | } 30 | 31 | return biggest.Interface() 32 | } 33 | -------------------------------------------------------------------------------- /slices/max_by.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // This method is like Max except that it accepts a function which 10 | // is invoked for each element in slice to return a number for comparison. 11 | // 12 | // example for 'function': 13 | // 14 | // type myObject struct { 15 | // rank int 16 | // } 17 | // 18 | // func returnRank(value1 interface{}) interface{} { 19 | // return value1.(myObject).rank 20 | // } 21 | // 22 | // Complexity: O(n) 23 | func MaxBy(slice interface{}, function func(interface{}) interface{}) interface{} { 24 | if !internal.SliceCheck(slice) { 25 | panic("'slice' is not slice type") 26 | } 27 | 28 | sliceValue := reflect.ValueOf(slice) 29 | 30 | if sliceValue.Len() == 0 { 31 | return nil 32 | } 33 | 34 | biggest := reflect.ValueOf(function(sliceValue.Index(0).Interface())).Interface() 35 | chosenElement := sliceValue.Index(0) 36 | for i := 0; i < sliceValue.Len(); i++ { 37 | element := sliceValue.Index(i) 38 | compareValue := reflect.ValueOf(function(element.Interface())).Interface() 39 | if res := internal.CompareNumbers(compareValue, biggest); res == internal.Higher { 40 | biggest = compareValue 41 | chosenElement = element 42 | } 43 | } 44 | 45 | return chosenElement.Interface() 46 | } 47 | -------------------------------------------------------------------------------- /slices/max_by_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TMaxBy struct { 13 | name string 14 | arr interface{} 15 | want interface{} 16 | } 17 | 18 | var tMaxByBenchs = []TMaxBy{ 19 | { 20 | name: "10", 21 | arr: []interface{}{}, 22 | }, 23 | { 24 | name: "100", 25 | arr: []interface{}{}, 26 | }, 27 | { 28 | name: "1000", 29 | arr: []interface{}{}, 30 | }, 31 | { 32 | name: "10000", 33 | arr: []interface{}{}, 34 | }, 35 | { 36 | name: "100000", 37 | arr: []interface{}{}, 38 | }, 39 | } 40 | 41 | func init() { 42 | for j := 0; j < len(tMaxByBenchs); j++ { 43 | length, _ := strconv.Atoi(tMaxByBenchs[j].name) 44 | for i := 0; i < length/10; i++ { 45 | tMaxByBenchs[j].arr = append(tMaxByBenchs[j].arr.([]interface{}), 0, 1, 2, 3, 4, 5, 6, 7, 8, 9) 46 | } 47 | } 48 | } 49 | 50 | func returnSameMaxByTest(value interface{}) interface{} { 51 | return value 52 | } 53 | 54 | func TestMaxBy(t *testing.T) { 55 | var tests = []TMaxBy{ 56 | { 57 | name: "nil", 58 | arr: nil, 59 | want: nil, 60 | }, 61 | { 62 | name: "empty", 63 | arr: []int{}, 64 | want: nil, 65 | }, 66 | { 67 | name: "normal", 68 | arr: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 69 | want: 9, 70 | }, 71 | { 72 | name: "it is middle", 73 | arr: []int{0, 1, 2, 3, 4, 5, 6, 7, 1, 2}, 74 | want: 7, 75 | }, 76 | { 77 | name: "interface", 78 | arr: []interface{}{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 79 | want: 9, 80 | }, 81 | } 82 | 83 | for _, subject := range tests { 84 | t.Run(subject.name, func(t *testing.T) { 85 | defer internal.DeferTestCases(t, subject.want) 86 | got := MaxBy(subject.arr, returnSameMaxByTest) 87 | 88 | if !generals.Same(got, subject.want) { 89 | t.Errorf("got = %v, wanted = %v", got, subject.want) 90 | return 91 | } 92 | }) 93 | } 94 | } 95 | 96 | func BenchmarkMaxBy(b *testing.B) { 97 | for j := 0; j < len(tMaxByBenchs); j++ { 98 | b.Run(fmt.Sprintf("slice_size_%s", tMaxByBenchs[j].name), func(b *testing.B) { 99 | for i := 0; i < b.N; i++ { 100 | MaxBy(tMaxByBenchs[j].arr, returnSameMaxByTest) 101 | } 102 | }) 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /slices/max_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TMax struct { 13 | name string 14 | arr interface{} 15 | want interface{} 16 | } 17 | 18 | var tMaxBenchs = []TMax{ 19 | { 20 | name: "10", 21 | arr: []interface{}{}, 22 | }, 23 | { 24 | name: "100", 25 | arr: []interface{}{}, 26 | }, 27 | { 28 | name: "1000", 29 | arr: []interface{}{}, 30 | }, 31 | { 32 | name: "10000", 33 | arr: []interface{}{}, 34 | }, 35 | { 36 | name: "100000", 37 | arr: []interface{}{}, 38 | }, 39 | } 40 | 41 | func init() { 42 | for j := 0; j < len(tMaxBenchs); j++ { 43 | length, _ := strconv.Atoi(tMaxBenchs[j].name) 44 | for i := 0; i < length/10; i++ { 45 | tMaxBenchs[j].arr = append(tMaxBenchs[j].arr.([]interface{}), []interface{}{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}...) 46 | } 47 | } 48 | } 49 | 50 | func TestMax(t *testing.T) { 51 | var tests = []TMax{ 52 | { 53 | name: "nil", 54 | arr: nil, 55 | want: nil, 56 | }, 57 | { 58 | name: "empty", 59 | arr: []interface{}{}, 60 | want: nil, 61 | }, 62 | { 63 | name: "just one item", 64 | arr: []interface{}{0}, 65 | want: 0, 66 | }, 67 | { 68 | name: "normal", 69 | arr: []interface{}{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 70 | want: 9, 71 | }, 72 | { 73 | name: "wrong data incompatible data types", 74 | arr: []interface{}{0, []interface{}{1, 2}, []interface{}{3, 4, 5}, []interface{}{6, 7}, 8, []interface{}{9}}, 75 | want: nil, 76 | }, 77 | { 78 | name: "type based", 79 | arr: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 80 | want: 9, 81 | }, 82 | } 83 | 84 | for _, subject := range tests { 85 | t.Run(subject.name, func(t *testing.T) { 86 | defer internal.DeferTestCases(t, subject.want) 87 | got := Max(subject.arr) 88 | 89 | if !generals.Same(got, subject.want) { 90 | t.Errorf("got = %v, wanted = %v", got, subject.want) 91 | return 92 | } 93 | }) 94 | } 95 | } 96 | 97 | func BenchmarkMax(b *testing.B) { 98 | for j := 0; j < len(tMaxBenchs); j++ { 99 | b.Run(fmt.Sprintf("slice_size_%s", tMaxBenchs[j].name), func(b *testing.B) { 100 | for i := 0; i < b.N; i++ { 101 | Max(tMaxBenchs[j].arr) 102 | } 103 | }) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /slices/mean.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Computes the mean of the values in slice. 10 | // 11 | // Complexity: O(n) 12 | func Mean(slice interface{}) float64 { 13 | if !internal.SliceCheck(slice) { 14 | panic("'slice' is not slice type") 15 | } 16 | 17 | sliceValue := reflect.ValueOf(slice) 18 | 19 | if sliceValue.Len() == 0 { 20 | return 0 21 | } 22 | 23 | floatType := reflect.TypeOf(1.0) 24 | sum := reflect.Zero(floatType) 25 | for i := 0; i < sliceValue.Len(); i++ { 26 | element := reflect.ValueOf(sliceValue.Index(i).Interface()) 27 | if internal.CanFloat(element.Interface()) { 28 | sum = reflect.ValueOf(sum.Float() + element.Float()) 29 | } else { 30 | sum = reflect.ValueOf(sum.Float() + element.Convert(floatType).Float()) 31 | } 32 | } 33 | 34 | average := sum.Float() / float64(sliceValue.Len()) 35 | 36 | return average 37 | } 38 | -------------------------------------------------------------------------------- /slices/mean_by.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // This method is like Mean except that it accepts a function 10 | // which is invoked for each element in slice to generate the 11 | // value to be averaged. 12 | // 13 | // example for 'function': 14 | // 15 | // type myObject struct { 16 | // rank int 17 | // } 18 | // 19 | // func returnRank(value1 interface{}) interface{} { 20 | // return value1.(myObject).rank 21 | // } 22 | // 23 | // Complexity: O(n) 24 | func MeanBy(slice interface{}, function func(interface{}) interface{}) float64 { 25 | if !internal.SliceCheck(slice) { 26 | panic("'slice' is not slice type") 27 | } 28 | 29 | sliceValue := reflect.ValueOf(slice) 30 | 31 | if sliceValue.Len() == 0 { 32 | return 0 33 | } 34 | 35 | floatType := reflect.TypeOf(1.0) 36 | sum := 0.0 37 | for i := 0; i < sliceValue.Len(); i++ { 38 | sum += reflect.ValueOf(function(sliceValue.Index(i).Interface())).Convert(floatType).Float() 39 | } 40 | 41 | return sum / float64(sliceValue.Len()) 42 | } 43 | -------------------------------------------------------------------------------- /slices/mean_by_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TMeanBy struct { 13 | name string 14 | arr interface{} 15 | want interface{} 16 | } 17 | 18 | var tMeanByBenchs = []TMeanBy{ 19 | { 20 | name: "10", 21 | arr: []interface{}{}, 22 | }, 23 | { 24 | name: "100", 25 | arr: []interface{}{}, 26 | }, 27 | { 28 | name: "1000", 29 | arr: []interface{}{}, 30 | }, 31 | { 32 | name: "10000", 33 | arr: []interface{}{}, 34 | }, 35 | { 36 | name: "100000", 37 | arr: []interface{}{}, 38 | }, 39 | } 40 | 41 | func init() { 42 | for j := 0; j < len(tMeanByBenchs); j++ { 43 | length, _ := strconv.Atoi(tMeanByBenchs[j].name) 44 | for i := 0; i < length/10; i++ { 45 | tMeanByBenchs[j].arr = append(tMeanByBenchs[j].arr.([]interface{}), []interface{}{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}...) 46 | } 47 | } 48 | } 49 | 50 | func returnSameMeanByTest(value interface{}) interface{} { 51 | return value 52 | } 53 | 54 | func TestMeanBy(t *testing.T) { 55 | var tests = []TMeanBy{ 56 | { 57 | name: "nil", 58 | arr: nil, 59 | want: nil, 60 | }, 61 | { 62 | name: "empty", 63 | arr: []interface{}{}, 64 | want: 0.0, 65 | }, 66 | { 67 | name: "just one item", 68 | arr: []int{0}, 69 | want: 0.0, 70 | }, 71 | { 72 | name: "normal", 73 | arr: []uint{1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 74 | want: 1.0, 75 | }, 76 | { 77 | name: "float", 78 | arr: []float64{0, 0.2, 0.1, 0.2, 0.1, 0.2, 0.2}, 79 | want: 1.0 / 7, 80 | }, 81 | { 82 | name: "type based", 83 | arr: []interface{}{1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 84 | want: 1.0, 85 | }, 86 | } 87 | 88 | for _, subject := range tests { 89 | t.Run(subject.name, func(t *testing.T) { 90 | defer internal.DeferTestCases(t, subject.want) 91 | got := MeanBy(subject.arr, returnSameMeanByTest) 92 | 93 | if !generals.Same(got, subject.want) { 94 | t.Errorf("got = %v, wanted = %v", got, subject.want) 95 | return 96 | } 97 | }) 98 | } 99 | } 100 | 101 | func BenchmarkMeanBy(b *testing.B) { 102 | for j := 0; j < len(tMeanByBenchs); j++ { 103 | b.Run(fmt.Sprintf("slice_size_%s", tMeanByBenchs[j].name), func(b *testing.B) { 104 | for i := 0; i < b.N; i++ { 105 | MeanBy(tMeanByBenchs[j].arr, returnSameMeanByTest) 106 | } 107 | }) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /slices/mean_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TMean struct { 13 | name string 14 | arr interface{} 15 | want interface{} 16 | } 17 | 18 | var tMeanBenchs = []TMean{ 19 | { 20 | name: "10", 21 | arr: []interface{}{}, 22 | }, 23 | { 24 | name: "100", 25 | arr: []interface{}{}, 26 | }, 27 | { 28 | name: "1000", 29 | arr: []interface{}{}, 30 | }, 31 | { 32 | name: "10000", 33 | arr: []interface{}{}, 34 | }, 35 | { 36 | name: "100000", 37 | arr: []interface{}{}, 38 | }, 39 | } 40 | 41 | func init() { 42 | for j := 0; j < len(tMeanBenchs); j++ { 43 | length, _ := strconv.Atoi(tMeanBenchs[j].name) 44 | for i := 0; i < length/10; i++ { 45 | tMeanBenchs[j].arr = append(tMeanBenchs[j].arr.([]interface{}), []interface{}{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}...) 46 | } 47 | } 48 | } 49 | 50 | func TestMean(t *testing.T) { 51 | var tests = []TMean{ 52 | { 53 | name: "nil", 54 | arr: nil, 55 | want: nil, 56 | }, 57 | { 58 | name: "empty", 59 | arr: []interface{}{}, 60 | want: 0.0, 61 | }, 62 | { 63 | name: "just one item", 64 | arr: []int{0}, 65 | want: 0.0, 66 | }, 67 | { 68 | name: "normal", 69 | arr: []uint{1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 70 | want: 1.0, 71 | }, 72 | { 73 | name: "float", 74 | arr: []float64{0, 0.2, 0.1, 0.2, 0.1, 0.2, 0.2}, 75 | want: 1.0 / 7, 76 | }, 77 | { 78 | name: "type based", 79 | arr: []interface{}{1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 80 | want: 1.0, 81 | }, 82 | } 83 | 84 | for _, subject := range tests { 85 | t.Run(subject.name, func(t *testing.T) { 86 | defer internal.DeferTestCases(t, subject.want) 87 | got := Mean(subject.arr) 88 | 89 | if !generals.Same(got, subject.want) { 90 | t.Errorf("got = %v, wanted = %v", got, subject.want) 91 | return 92 | } 93 | }) 94 | } 95 | } 96 | 97 | func BenchmarkMean(b *testing.B) { 98 | for j := 0; j < len(tMeanBenchs); j++ { 99 | b.Run(fmt.Sprintf("slice_size_%s", tMeanBenchs[j].name), func(b *testing.B) { 100 | for i := 0; i < b.N; i++ { 101 | Mean(tMeanBenchs[j].arr) 102 | } 103 | }) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /slices/min.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Computes the minimum value of slice. 10 | // 11 | // Complexity: O(n) 12 | func Min(slice interface{}) interface{} { 13 | if !internal.SliceCheck(slice) { 14 | panic("'slice' is not slice type") 15 | } 16 | 17 | sliceValue := reflect.ValueOf(slice) 18 | 19 | if sliceValue.Len() == 0 { 20 | return nil 21 | } 22 | 23 | biggest := sliceValue.Index(0) 24 | for i := 0; i < sliceValue.Len(); i++ { 25 | element := sliceValue.Index(i) 26 | if res := internal.CompareNumbers(element.Interface(), biggest.Interface()); res == internal.Lower { 27 | biggest = element 28 | } 29 | } 30 | 31 | return biggest.Interface() 32 | } 33 | -------------------------------------------------------------------------------- /slices/min_by.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // This method is like Min except that it accepts a function which 10 | // is invoked for each element in slice to return a number for comparison. 11 | // 12 | // example for 'function': 13 | // 14 | // type myObject struct { 15 | // rank int 16 | // } 17 | // 18 | // func returnRank(value1 interface{}) interface{} { 19 | // return value1.(myObject).rank 20 | // } 21 | // 22 | // Complexity: O(n) 23 | func MinBy(slice interface{}, function func(interface{}) interface{}) interface{} { 24 | if !internal.SliceCheck(slice) { 25 | panic("'slice' is not slice type") 26 | } 27 | 28 | sliceValue := reflect.ValueOf(slice) 29 | 30 | if sliceValue.Len() == 0 { 31 | return nil 32 | } 33 | 34 | biggest := reflect.ValueOf(function(sliceValue.Index(0).Interface())).Interface() 35 | chosenElement := sliceValue.Index(0) 36 | for i := 0; i < sliceValue.Len(); i++ { 37 | element := sliceValue.Index(i) 38 | compareValue := reflect.ValueOf(function(element.Interface())).Interface() 39 | if res := internal.CompareNumbers(compareValue, biggest); res == internal.Lower { 40 | biggest = compareValue 41 | chosenElement = element 42 | } 43 | } 44 | 45 | return chosenElement.Interface() 46 | } 47 | -------------------------------------------------------------------------------- /slices/min_by_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TMinBy struct { 13 | name string 14 | arr interface{} 15 | want interface{} 16 | } 17 | 18 | var tMinByBenchs = []TMinBy{ 19 | { 20 | name: "10", 21 | arr: []interface{}{}, 22 | }, 23 | { 24 | name: "100", 25 | arr: []interface{}{}, 26 | }, 27 | { 28 | name: "1000", 29 | arr: []interface{}{}, 30 | }, 31 | { 32 | name: "10000", 33 | arr: []interface{}{}, 34 | }, 35 | { 36 | name: "100000", 37 | arr: []interface{}{}, 38 | }, 39 | } 40 | 41 | func init() { 42 | for j := 0; j < len(tMinByBenchs); j++ { 43 | length, _ := strconv.Atoi(tMinByBenchs[j].name) 44 | for i := 0; i < length/10; i++ { 45 | tMinByBenchs[j].arr = append(tMinByBenchs[j].arr.([]interface{}), 0, 1, 2, 3, 4, 5, 6, 7, 8, 9) 46 | } 47 | } 48 | } 49 | 50 | func returnSameMinByTest(value interface{}) interface{} { 51 | return value 52 | } 53 | 54 | func TestMinBy(t *testing.T) { 55 | var tests = []TMinBy{ 56 | { 57 | name: "nil", 58 | arr: nil, 59 | want: nil, 60 | }, 61 | { 62 | name: "empty", 63 | arr: []int{}, 64 | want: nil, 65 | }, 66 | { 67 | name: "normal", 68 | arr: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 69 | want: 0, 70 | }, 71 | { 72 | name: "it is middle", 73 | arr: []int{1, 2, 3, 0, 4, 5, 6, 7, 1, 2}, 74 | want: 0, 75 | }, 76 | { 77 | name: "interface", 78 | arr: []interface{}{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 79 | want: 0, 80 | }, 81 | } 82 | 83 | for _, subject := range tests { 84 | t.Run(subject.name, func(t *testing.T) { 85 | defer internal.DeferTestCases(t, subject.want) 86 | got := MinBy(subject.arr, returnSameMinByTest) 87 | 88 | if !generals.Same(got, subject.want) { 89 | t.Errorf("got = %v, wanted = %v", got, subject.want) 90 | return 91 | } 92 | }) 93 | } 94 | } 95 | 96 | func BenchmarkMinBy(b *testing.B) { 97 | for j := 0; j < len(tMinByBenchs); j++ { 98 | b.Run(fmt.Sprintf("slice_size_%s", tMinByBenchs[j].name), func(b *testing.B) { 99 | for i := 0; i < b.N; i++ { 100 | MinBy(tMinByBenchs[j].arr, returnSameMinByTest) 101 | } 102 | }) 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /slices/min_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TMin struct { 13 | name string 14 | arr interface{} 15 | want interface{} 16 | } 17 | 18 | var tMinBenchs = []TMin{ 19 | { 20 | name: "10", 21 | arr: []interface{}{}, 22 | }, 23 | { 24 | name: "100", 25 | arr: []interface{}{}, 26 | }, 27 | { 28 | name: "1000", 29 | arr: []interface{}{}, 30 | }, 31 | { 32 | name: "10000", 33 | arr: []interface{}{}, 34 | }, 35 | { 36 | name: "100000", 37 | arr: []interface{}{}, 38 | }, 39 | } 40 | 41 | func init() { 42 | for j := 0; j < len(tMinBenchs); j++ { 43 | length, _ := strconv.Atoi(tMinBenchs[j].name) 44 | for i := 0; i < length/10; i++ { 45 | tMinBenchs[j].arr = append(tMinBenchs[j].arr.([]interface{}), []interface{}{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}...) 46 | } 47 | } 48 | } 49 | 50 | func TestMin(t *testing.T) { 51 | var tests = []TMin{ 52 | { 53 | name: "nil", 54 | arr: nil, 55 | want: nil, 56 | }, 57 | { 58 | name: "empty", 59 | arr: []interface{}{}, 60 | want: nil, 61 | }, 62 | { 63 | name: "just one item", 64 | arr: []interface{}{0}, 65 | want: 0, 66 | }, 67 | { 68 | name: "normal", 69 | arr: []interface{}{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 70 | want: 0, 71 | }, 72 | { 73 | name: "wrong data incompatible data types", 74 | arr: []interface{}{0, []interface{}{1, 2}, []interface{}{3, 4, 5}, []interface{}{6, 7}, 8, []interface{}{9}}, 75 | want: nil, 76 | }, 77 | { 78 | name: "type based", 79 | arr: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 80 | want: 0, 81 | }, 82 | } 83 | 84 | for _, subject := range tests { 85 | t.Run(subject.name, func(t *testing.T) { 86 | defer internal.DeferTestCases(t, subject.want) 87 | got := Min(subject.arr) 88 | 89 | if !generals.Same(got, subject.want) { 90 | t.Errorf("got = %v, wanted = %v", got, subject.want) 91 | return 92 | } 93 | }) 94 | } 95 | } 96 | 97 | func BenchmarkMin(b *testing.B) { 98 | for j := 0; j < len(tMinBenchs); j++ { 99 | b.Run(fmt.Sprintf("slice_size_%s", tMinBenchs[j].name), func(b *testing.B) { 100 | for i := 0; i < b.N; i++ { 101 | Min(tMinBenchs[j].arr) 102 | } 103 | }) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /slices/n_th.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Gets the (n)th element of a slice. If n is negative, the (nth) 10 | // element from the end is returned. 11 | // 12 | // Complexity: O(1) 13 | func Nth(slice interface{}, index int) interface{} { 14 | if !internal.SliceCheck(slice) { 15 | panic("passed 'slice' variable is not slice type") 16 | } 17 | 18 | values := reflect.ValueOf(slice) 19 | if index < 0 { 20 | index = values.Len() + index 21 | } 22 | 23 | if index >= values.Len() { 24 | panic("index out of range") 25 | } 26 | 27 | if values.Len() == 0 { 28 | return nil 29 | } 30 | 31 | return values.Index(index).Interface() 32 | } 33 | -------------------------------------------------------------------------------- /slices/n_th_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TNth struct { 13 | name string 14 | arg1 interface{} 15 | arg2 int 16 | expected interface{} 17 | } 18 | 19 | var TNthBenchs = []TNth{ 20 | { 21 | name: "10", 22 | arg1: []interface{}{}, 23 | }, 24 | { 25 | name: "100", 26 | arg1: []interface{}{}, 27 | }, 28 | { 29 | name: "1000", 30 | arg1: []interface{}{}, 31 | }, 32 | { 33 | name: "10000", 34 | arg1: []interface{}{}, 35 | }, 36 | { 37 | name: "100000", 38 | arg1: []interface{}{}, 39 | }, 40 | } 41 | 42 | func init() { 43 | for i := 0; i < len(TNthBenchs); i++ { 44 | k, _ := strconv.Atoi(TNthBenchs[i].name) 45 | for j := 0; j < k/10; j++ { 46 | TNthBenchs[i].arg1 = append(TNthBenchs[i].arg1.([]interface{}), []interface{}{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}...) 47 | } 48 | } 49 | } 50 | 51 | func TestNth(t *testing.T) { 52 | tests := []TNth{ 53 | { 54 | name: "nil", 55 | arg1: nil, 56 | arg2: 4, 57 | expected: nil, 58 | }, 59 | { 60 | name: "empty", 61 | arg1: []interface{}{}, 62 | arg2: -1, 63 | expected: nil, 64 | }, 65 | { 66 | name: "default", 67 | arg1: []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, 68 | arg2: 5, 69 | expected: 6, 70 | }, 71 | { 72 | name: "default1", 73 | arg1: []interface{}{"a", "b", "c", "d", "e", "f", "u"}, 74 | arg2: -2, 75 | expected: "f", 76 | }, 77 | } 78 | 79 | for _, sample := range tests { 80 | t.Run(sample.name, func(t *testing.T) { 81 | defer internal.DeferTestCases(t, sample.expected) 82 | got := Nth(sample.arg1, sample.arg2) 83 | 84 | if !generals.Same(got, sample.expected) { 85 | t.Errorf("got = %v, wanted = %v", got, sample.expected) 86 | return 87 | } 88 | }) 89 | } 90 | } 91 | 92 | func BenchmarkNth(b *testing.B) { 93 | for _, sample := range TNthBenchs { 94 | b.Run(fmt.Sprintf("input_size_%s", sample.name), func(b *testing.B) { 95 | for i := 0; i < b.N; i++ { 96 | Nth(sample.arg1, sample.arg2) 97 | } 98 | }) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /slices/pull.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/generals" 7 | "github.com/golodash/godash/internal" 8 | ) 9 | 10 | // Removes all given values from slice. 11 | // 12 | // Complexity: O(n*m) 13 | // 14 | // n = length of 'slice' 15 | // 16 | // m = length of 'values' 17 | func Pull(slice, values interface{}) interface{} { 18 | if !internal.SliceCheck(slice) { 19 | panic("passed 'slice' variable is not slice type") 20 | } 21 | if !internal.SliceCheck(values) { 22 | panic("passed 'values' variable is not slice type") 23 | } 24 | 25 | sliceValue := reflect.ValueOf(slice) 26 | outputValue := reflect.MakeSlice(sliceValue.Type(), 0, sliceValue.Len()) 27 | valuesValue := reflect.ValueOf(values) 28 | for i := 0; i < sliceValue.Len(); i++ { 29 | add := true 30 | for j := 0; j < valuesValue.Len(); j++ { 31 | if generals.Same(sliceValue.Index(i).Interface(), valuesValue.Index(j).Interface()) { 32 | add = false 33 | break 34 | } 35 | } 36 | 37 | if add { 38 | outputValue = reflect.Append(outputValue, sliceValue.Index(i)) 39 | } 40 | } 41 | 42 | return outputValue.Interface() 43 | } 44 | -------------------------------------------------------------------------------- /slices/pull_at.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | "sort" 6 | 7 | "github.com/golodash/godash/internal" 8 | ) 9 | 10 | // Removes elements from slice corresponding to indexes and returns a slice of 11 | // remaining elements and removed elements. 12 | // 13 | // Note: Duplicate key numbers in 'remSlice' variable will get removed 14 | // 15 | // Worst-Case Complexity: O(n*log(n)) 16 | // 17 | // This complexity is mainly because of sorting 'remSlice'. 18 | // 19 | // Keep it sorted to have a better complexity of: 20 | // 21 | // Best-Case Complexity: O(n) 22 | func PullAt(slice interface{}, remSlice []int) (interface{}, interface{}) { 23 | if !internal.SliceCheck(slice) { 24 | panic("passed 'slice' variable is not slice type") 25 | } 26 | 27 | if !sort.SliceIsSorted(remSlice, func(i, j int) bool { return remSlice[i] < remSlice[j] }) { 28 | sort.Ints(remSlice) 29 | } 30 | 31 | remSlice = internal.UniqueInt(remSlice) 32 | sliceValue := reflect.ValueOf(slice) 33 | removed := reflect.MakeSlice(sliceValue.Type(), 0, sliceValue.Len()) 34 | for i := 0; i < len(remSlice); i++ { 35 | if !(remSlice[i]-i < sliceValue.Len()) { 36 | break 37 | } 38 | removed = reflect.Append(removed, reflect.ValueOf(sliceValue.Index(remSlice[i]-i).Interface())) 39 | sliceValue = reflect.AppendSlice(sliceValue.Slice(0, remSlice[i]-i), sliceValue.Slice(remSlice[i]+1-i, sliceValue.Len())) 40 | } 41 | 42 | return sliceValue.Interface(), removed.Interface() 43 | } 44 | -------------------------------------------------------------------------------- /slices/remove_by.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Removes all elements from slice that the passed function returns true on them 10 | // and returns a slice of remaining elements and a slice of removed elements. 11 | // The passed function will invoke with one argument. 12 | // 13 | // example for 'function': 14 | // 15 | // func isOdd(n interface{}) bool { 16 | // return n.(int)%2 != 0 17 | // } 18 | // 19 | // Complexity: O(n) 20 | func RemoveBy(slice interface{}, function func(interface{}) bool) (interface{}, interface{}) { 21 | if !internal.SliceCheck(slice) { 22 | panic("passed 'slice' variable is not slice type") 23 | } 24 | 25 | sliceValue := reflect.ValueOf(slice) 26 | removed := reflect.MakeSlice(reflect.TypeOf(slice), 0, sliceValue.Len()) 27 | for i := sliceValue.Len() - 1; i >= 0; i-- { 28 | item := sliceValue.Index(i) 29 | if function(item.Interface()) { 30 | removed = reflect.Append(removed, item) 31 | sliceValue = reflect.AppendSlice(sliceValue.Slice(0, i), sliceValue.Slice(i+1, sliceValue.Len())) 32 | } 33 | } 34 | 35 | return sliceValue.Interface(), removed.Interface() 36 | } 37 | -------------------------------------------------------------------------------- /slices/reverse.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Reverses slice so that the first element becomes the last, 10 | // the second element becomes the second to last, and so on. 11 | // 12 | // Complexity: O(n) 13 | func Reverse(slice interface{}) interface{} { 14 | if !internal.SliceCheck(slice) { 15 | panic("passed 'slice' variable is not slice type") 16 | } 17 | 18 | length := reflect.ValueOf(slice).Len() 19 | swapper := reflect.MakeSlice(reflect.TypeOf(slice), length, length).Interface() 20 | reflect.Copy(reflect.ValueOf(swapper), reflect.ValueOf(slice)) 21 | swap := reflect.Swapper(swapper) 22 | for i, j := 0, length-1; i < j; i, j = i+1, j-1 { 23 | swap(i, j) 24 | } 25 | 26 | return swapper 27 | } 28 | -------------------------------------------------------------------------------- /slices/reverse_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TReverse struct { 13 | name string 14 | arg interface{} 15 | expected interface{} 16 | } 17 | 18 | var TReverseBenchs = []TReverse{ 19 | { 20 | name: "10", 21 | arg: []interface{}{}, 22 | }, 23 | { 24 | name: "100", 25 | arg: []interface{}{}, 26 | }, 27 | { 28 | name: "1000", 29 | arg: []interface{}{}, 30 | }, 31 | { 32 | name: "10000", 33 | arg: []interface{}{}, 34 | }, 35 | { 36 | name: "100000", 37 | arg: []interface{}{}, 38 | }, 39 | } 40 | 41 | func init() { 42 | for i := 0; i < len(TReverseBenchs); i++ { 43 | k, _ := strconv.Atoi(TReverseBenchs[i].name) 44 | for j := 0; j < k/10; j++ { 45 | TReverseBenchs[i].arg = append(TReverseBenchs[i].arg.([]interface{}), []interface{}{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}...) 46 | } 47 | } 48 | } 49 | 50 | func TestReverse(t *testing.T) { 51 | var tests = []TReverse{ 52 | { 53 | name: "nil", 54 | arg: nil, 55 | expected: nil, 56 | }, 57 | { 58 | name: "empty", 59 | arg: []interface{}{}, 60 | expected: []interface{}{}, 61 | }, 62 | { 63 | name: "default", 64 | arg: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, 65 | expected: []int{0, 9, 8, 7, 6, 5, 4, 3, 2, 1}, 66 | }, 67 | { 68 | name: "default1", 69 | arg: []string{"a", "b", "c", "d", "e", "f", "u"}, 70 | expected: []string{"u", "f", "e", "d", "c", "b", "a"}, 71 | }, 72 | } 73 | for _, sample := range tests { 74 | t.Run(sample.name, func(t *testing.T) { 75 | defer internal.DeferTestCases(t, sample.expected) 76 | got := Reverse(sample.arg) 77 | 78 | if !generals.Same(got, sample.expected) { 79 | t.Errorf("got = %v, wanted = %v", got, sample.expected) 80 | return 81 | } 82 | }) 83 | } 84 | } 85 | 86 | func BenchmarkReverse(b *testing.B) { 87 | for _, sample := range TReverseBenchs { 88 | b.Run(fmt.Sprintf("input_size_%s", sample.name), func(b *testing.B) { 89 | for i := 0; i < b.N; i++ { 90 | Reverse(sample.arg) 91 | } 92 | }) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /slices/slice.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Creates a slice from a slice from 'start' up to 'to', but not including, end. 10 | // 11 | // Complexity: O(1) 12 | func Slice(slice interface{}, from, to int) interface{} { 13 | if !internal.SliceCheck(slice) { 14 | panic("passed 'slice' variable is not slice type") 15 | } 16 | 17 | sliceValue := reflect.ValueOf(slice) 18 | if from < 0 { 19 | from = sliceValue.Len() + from 20 | } 21 | if to < 0 { 22 | to = sliceValue.Len() + to 23 | } 24 | 25 | if from > to { 26 | panic("'from' is bigger than 'to'") 27 | } 28 | 29 | if from >= sliceValue.Len() && from != 0 { 30 | panic("'from' should be in range of 'slice'") 31 | } 32 | if to > sliceValue.Len() { 33 | panic("'to' should be in range of 'slice'") 34 | } 35 | 36 | if sliceValue.Len() == 0 { 37 | return slice 38 | } 39 | 40 | return sliceValue.Slice(from, to).Interface() 41 | } 42 | -------------------------------------------------------------------------------- /slices/sorted_index_of_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TSortedIndexOf struct { 13 | name string 14 | arr interface{} 15 | value interface{} 16 | want interface{} 17 | } 18 | 19 | var tSortedIndexOfBenchs = []TSortedIndexOf{ 20 | { 21 | name: "10", 22 | arr: []int{}, 23 | value: 0, 24 | }, 25 | { 26 | name: "100", 27 | arr: []int{}, 28 | value: 0, 29 | }, 30 | { 31 | name: "1000", 32 | arr: []int{}, 33 | value: 0, 34 | }, 35 | { 36 | name: "10000", 37 | arr: []int{}, 38 | value: 0, 39 | }, 40 | { 41 | name: "100000", 42 | arr: []int{}, 43 | value: 0, 44 | }, 45 | } 46 | 47 | func init() { 48 | for j := 0; j < len(tSortedIndexOfBenchs); j++ { 49 | length, _ := strconv.Atoi(tSortedIndexOfBenchs[j].name) 50 | for i := 0; i < length/10; i++ { 51 | tSortedIndexOfBenchs[j].arr = append(tSortedIndexOfBenchs[j].arr.([]int), 0+(i*10), 1+(i*10), 2+(i*10), 3+(i*10), 4+(i*10), 5+(i*10), 6+(i*10), 7+(i*10), 8+(i*10), 9+(i*10)) 52 | } 53 | } 54 | } 55 | 56 | func TestSortedIndexOf(t *testing.T) { 57 | tests := []TSortedIndexOf{ 58 | { 59 | name: "nil", 60 | arr: nil, 61 | value: 0, 62 | want: nil, 63 | }, 64 | { 65 | name: "empty", 66 | arr: []int{}, 67 | value: 5, 68 | want: -1, 69 | }, 70 | { 71 | name: "normal", 72 | arr: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 73 | value: 3, 74 | want: 3, 75 | }, 76 | { 77 | name: "more sequence", 78 | arr: []int{0, 1, 2, 3, 3, 3, 4, 5, 6, 7, 8, 9}, 79 | value: 3, 80 | want: 3, 81 | }, 82 | { 83 | name: "fail", 84 | arr: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 85 | value: 88, 86 | want: -1, 87 | }, 88 | } 89 | 90 | for _, subject := range tests { 91 | t.Run(subject.name, func(t *testing.T) { 92 | defer internal.DeferTestCases(t, subject.want) 93 | got := SortedIndexOf(subject.arr, subject.value) 94 | 95 | if !generals.Same(got, subject.want) { 96 | t.Errorf("got = %v, wanted = %v", got, subject.want) 97 | return 98 | } 99 | }) 100 | } 101 | } 102 | 103 | func BenchmarkSortedIndexOf(b *testing.B) { 104 | for j := 0; j < len(tSortedIndexOfBenchs); j++ { 105 | b.Run(fmt.Sprintf("slice_size_%s", tSortedIndexOfBenchs[j].name), func(b *testing.B) { 106 | for i := 0; i < b.N; i++ { 107 | SortedIndexOf(tSortedIndexOfBenchs[j].arr, tSortedIndexOfBenchs[j].value) 108 | } 109 | }) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /slices/sorted_last_index_of.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // This method is like LastIndexOf except that it performs a 10 | // binary search on a sorted slice. 11 | // 12 | // Complexity: O(log(n)) 13 | func SortedLastIndexOf(slice, value interface{}) int { 14 | if !internal.SliceCheck(slice) { 15 | panic("passed 'slice' variable is not slice type") 16 | } 17 | 18 | sType := reflect.TypeOf(slice) 19 | val := reflect.ValueOf(value) 20 | if val.Type().Kind() != sType.Elem().Kind() && sType.Elem().Kind() != reflect.Interface { 21 | panic("'value' is not compatible with 'slice' elements") 22 | } 23 | 24 | return sortedLastIndexOf(slice, value, compareHigherEqual, compareIsEqual) 25 | } 26 | 27 | // Complexity: O(log(n)) 28 | func sortedLastIndexOf(slice, value, isHigherEqualFunction, isEqualFunction interface{}) int { 29 | sliceValue := reflect.ValueOf(slice) 30 | len := sliceValue.Len() 31 | 32 | if len == 0 { 33 | return -1 34 | } else if len == 1 { 35 | item := sliceValue.Index(0) 36 | if res := reflect.ValueOf(isEqualFunction).Call([]reflect.Value{item, reflect.ValueOf(value)}); res[0].Bool() { 37 | return 0 38 | } else { 39 | return -1 40 | } 41 | } else if len == 2 { 42 | item0 := sliceValue.Index(0) 43 | item1 := sliceValue.Index(1) 44 | if res := reflect.ValueOf(isEqualFunction).Call([]reflect.Value{item0, reflect.ValueOf(value)}); res[0].Bool() { 45 | return 0 46 | } else if res := reflect.ValueOf(isEqualFunction).Call([]reflect.Value{item1, reflect.ValueOf(value)}); res[0].Bool() { 47 | return 1 48 | } else { 49 | return -1 50 | } 51 | } 52 | 53 | item := sliceValue.Index(len / 2).Interface() 54 | 55 | if !internal.AreComparable(item, value) { 56 | panic("couldn't compare 'value' with all items in passed slice") 57 | } 58 | 59 | var result int 60 | if res := reflect.ValueOf(isHigherEqualFunction).Call([]reflect.Value{reflect.ValueOf(item), reflect.ValueOf(value)}); res[0].Bool() { 61 | if result = sortedLastIndexOf(sliceValue.Slice(len/2, len).Interface(), value, isHigherEqualFunction, isEqualFunction); result == -1 { 62 | return -1 63 | } 64 | 65 | return result + (len / 2) 66 | } else { 67 | if result = sortedLastIndexOf(sliceValue.Slice(0, (len/2)+1).Interface(), value, isHigherEqualFunction, isEqualFunction); result == -1 { 68 | return -1 69 | } 70 | 71 | return result 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /slices/sorted_unique.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // This function creates a duplicate-free version of an slice. 10 | // 11 | // This method is designed and optimized for sorted slices. 12 | // 13 | // Complexity: O(n) 14 | func SortedUnique(slice interface{}) interface{} { 15 | if !internal.SliceCheck(slice) { 16 | panic("passed 'slice' variable is not slice type") 17 | } 18 | 19 | sliceItemType := reflect.TypeOf(slice).Elem() 20 | sliceValue := reflect.ValueOf(slice) 21 | tempMap := reflect.MakeMap(reflect.MapOf(sliceItemType, reflect.TypeOf(true))) 22 | output := reflect.MakeSlice(reflect.TypeOf(slice), 0, sliceValue.Len()) 23 | 24 | for i := 0; i < sliceValue.Len(); i++ { 25 | item := sliceValue.Index(i) 26 | if exist := tempMap.MapIndex(item); !exist.IsValid() { 27 | tempMap.SetMapIndex(item, reflect.ValueOf(true)) 28 | output = reflect.Append(output, item) 29 | } 30 | } 31 | 32 | return output.Interface() 33 | } 34 | -------------------------------------------------------------------------------- /slices/sorted_unique_by.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // This method is like 'UniqueBy' except that it's designed and optimized 10 | // for sorted slices. 11 | // 12 | // It accepts a function which is invoked for each element in slice to 13 | // generate the criterion by which uniqueness is computed. 14 | // 15 | // example for 'function': 16 | // 17 | // func makeInt(input interface{}) interface{} { 18 | // return int(input.(float64)) 19 | // } 20 | // 21 | // Complexity: O(n) 22 | func SortedUniqueBy(slice interface{}, function func(interface{}) interface{}) interface{} { 23 | if !internal.SliceCheck(slice) { 24 | panic("passed 'slice' variable is not slice type") 25 | } 26 | 27 | sliceType := reflect.TypeOf(slice) 28 | sliceValue := reflect.ValueOf(slice) 29 | output := reflect.MakeSlice(sliceType, 0, sliceValue.Len()) 30 | 31 | sliceItemType := sliceType.Elem() 32 | tempMap := reflect.MakeMap(reflect.MapOf(sliceItemType, reflect.TypeOf(true))) 33 | for i := 0; i < sliceValue.Len(); i++ { 34 | item := reflect.ValueOf(sliceValue.Index(i).Interface()) 35 | key := function(item.Interface()) 36 | if exist := tempMap.MapIndex(reflect.ValueOf(key)); !exist.IsValid() { 37 | tempMap.SetMapIndex(reflect.ValueOf(key), reflect.ValueOf(true)) 38 | output = reflect.Append(output, item) 39 | } 40 | } 41 | 42 | return output.Interface() 43 | } 44 | -------------------------------------------------------------------------------- /slices/sorted_unique_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TSortedUnique struct { 13 | name string 14 | arg1 interface{} 15 | expected interface{} 16 | } 17 | 18 | var TSortedUniqueBenchs = []TSortedUnique{ 19 | { 20 | name: "10", 21 | arg1: []int{}, 22 | }, 23 | { 24 | name: "100", 25 | arg1: []int{}, 26 | }, 27 | { 28 | name: "1000", 29 | arg1: []int{}, 30 | }, 31 | { 32 | name: "10000", 33 | arg1: []int{}, 34 | }, 35 | { 36 | name: "100000", 37 | arg1: []int{}, 38 | }, 39 | } 40 | 41 | func init() { 42 | for i := 0; i < len(TSortedUniqueBenchs); i++ { 43 | k, _ := strconv.Atoi(TSortedUniqueBenchs[i].name) 44 | for j := 0; j < k/10; j++ { 45 | TSortedUniqueBenchs[i].arg1 = append(TSortedUniqueBenchs[i].arg1.([]int), 1, 2, 3, 4, 5, 6, 7, 8, 9, 0) 46 | } 47 | } 48 | } 49 | 50 | func TestSortedUnique(t *testing.T) { 51 | var tests = []TSortedUnique{ 52 | { 53 | name: "nil", 54 | arg1: nil, 55 | expected: nil, 56 | }, 57 | { 58 | name: "empty", 59 | arg1: []interface{}{}, 60 | expected: []interface{}{}, 61 | }, 62 | { 63 | name: "default", 64 | arg1: []interface{}{1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 0}, 65 | expected: []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, 66 | }, 67 | { 68 | name: "type based", 69 | arg1: []string{"A", "B", "C", "D", "E", "F", "U", "U", "U"}, 70 | expected: []string{"A", "B", "C", "D", "E", "F", "U"}, 71 | }, 72 | } 73 | 74 | for _, sample := range tests { 75 | t.Run(sample.name, func(t *testing.T) { 76 | defer internal.DeferTestCases(t, sample.expected) 77 | got := SortedUnique(sample.arg1) 78 | 79 | if !generals.Same(got, sample.expected) { 80 | t.Errorf("got = %v, wanted = %v", got, sample.expected) 81 | return 82 | } 83 | }) 84 | } 85 | } 86 | 87 | func BenchmarkSortedUnique(b *testing.B) { 88 | for _, sample := range TSortedUniqueBenchs { 89 | b.Run(fmt.Sprintf("input_size_%s", sample.name), func(b *testing.B) { 90 | for i := 0; i < b.N; i++ { 91 | SortedUnique(sample.arg1) 92 | } 93 | }) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /slices/sum.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Computes the sum of the values in slice. 10 | // 11 | // Complexity: O(n) 12 | func Sum(slice interface{}) interface{} { 13 | if !internal.SliceCheck(slice) { 14 | panic("'slice' is not slice type") 15 | } 16 | 17 | sliceValue := reflect.ValueOf(slice) 18 | sliceElementType := sliceValue.Type().Elem() 19 | 20 | if sliceElementType.Kind() == reflect.Interface { 21 | sliceElementType = reflect.TypeOf(1.0) 22 | } 23 | 24 | if sliceValue.Len() == 0 { 25 | return reflect.Zero(sliceElementType).Interface() 26 | } 27 | 28 | floatType := reflect.TypeOf(1.0) 29 | sum := reflect.Zero(floatType) 30 | for i := 0; i < sliceValue.Len(); i++ { 31 | element := reflect.ValueOf(sliceValue.Index(i).Interface()) 32 | if internal.CanFloat(element.Interface()) { 33 | sum = reflect.ValueOf(sum.Float() + element.Float()) 34 | } else { 35 | sum = reflect.ValueOf(sum.Float() + element.Convert(floatType).Float()) 36 | } 37 | } 38 | 39 | return sum.Convert(sliceElementType).Interface() 40 | } 41 | -------------------------------------------------------------------------------- /slices/sum_by.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // This method is like Sum except that it accepts a function 10 | // which is invoked for each element in slice to generate the 11 | // value to be summed. 12 | // 13 | // example for 'function': 14 | // 15 | // type myObject struct { 16 | // rank int 17 | // } 18 | // 19 | // func returnRank(value1 interface{}) interface{} { 20 | // return value1.(myObject).rank 21 | // } 22 | // 23 | // Complexity: O(n) 24 | func SumBy(slice interface{}, function func(interface{}) interface{}) float64 { 25 | if !internal.SliceCheck(slice) { 26 | panic("'slice' is not slice type") 27 | } 28 | 29 | sliceValue := reflect.ValueOf(slice) 30 | 31 | if sliceValue.Len() == 0 { 32 | return 0.0 33 | } 34 | 35 | floatType := reflect.TypeOf(1.0) 36 | sum := 0.0 37 | for i := 0; i < sliceValue.Len(); i++ { 38 | element := reflect.ValueOf(function(sliceValue.Index(i).Interface())).Convert(floatType).Float() 39 | 40 | sum = sum + element 41 | } 42 | 43 | return sum 44 | } 45 | -------------------------------------------------------------------------------- /slices/sum_by_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TSumBy struct { 13 | name string 14 | arr interface{} 15 | want interface{} 16 | } 17 | 18 | var tSumByBenchs = []TSumBy{ 19 | { 20 | name: "10", 21 | arr: []interface{}{}, 22 | }, 23 | { 24 | name: "100", 25 | arr: []interface{}{}, 26 | }, 27 | { 28 | name: "1000", 29 | arr: []interface{}{}, 30 | }, 31 | { 32 | name: "10000", 33 | arr: []interface{}{}, 34 | }, 35 | { 36 | name: "100000", 37 | arr: []interface{}{}, 38 | }, 39 | } 40 | 41 | func init() { 42 | for j := 0; j < len(tSumByBenchs); j++ { 43 | length, _ := strconv.Atoi(tSumByBenchs[j].name) 44 | for i := 0; i < length/10; i++ { 45 | tSumByBenchs[j].arr = append(tSumByBenchs[j].arr.([]interface{}), []interface{}{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}...) 46 | } 47 | } 48 | } 49 | 50 | func returnSameSumByTest(value interface{}) interface{} { 51 | return value 52 | } 53 | 54 | func TestSumBy(t *testing.T) { 55 | var tests = []TSumBy{ 56 | { 57 | name: "nil", 58 | arr: nil, 59 | want: nil, 60 | }, 61 | { 62 | name: "empty", 63 | arr: []interface{}{}, 64 | want: 0.0, 65 | }, 66 | { 67 | name: "just one item", 68 | arr: []int{0}, 69 | want: 0.0, 70 | }, 71 | { 72 | name: "normal", 73 | arr: []uint{1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 74 | want: 10.0, 75 | }, 76 | { 77 | name: "wrong data incompatible data types", 78 | arr: []float64{0, 0.2, 0.1, 0.2, 0.1, 0.2, 0.2}, 79 | want: 1.0, 80 | }, 81 | { 82 | name: "type based", 83 | arr: []interface{}{1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 84 | want: 10.0, 85 | }, 86 | } 87 | 88 | for _, subject := range tests { 89 | t.Run(subject.name, func(t *testing.T) { 90 | defer internal.DeferTestCases(t, subject.want) 91 | got := SumBy(subject.arr, returnSameSumByTest) 92 | 93 | if !generals.Same(got, subject.want) { 94 | t.Errorf("got = %v, wanted = %v", got, subject.want) 95 | return 96 | } 97 | }) 98 | } 99 | } 100 | 101 | func BenchmarkSumBy(b *testing.B) { 102 | for j := 0; j < len(tSumByBenchs); j++ { 103 | b.Run(fmt.Sprintf("slice_size_%s", tSumByBenchs[j].name), func(b *testing.B) { 104 | for i := 0; i < b.N; i++ { 105 | SumBy(tSumByBenchs[j].arr, returnSameSumByTest) 106 | } 107 | }) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /slices/tail.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Returns all but the first element of slice. 10 | // 11 | // Complexity: O(1) 12 | func Tail(slice interface{}) interface{} { 13 | if !internal.SliceCheck(slice) { 14 | panic("passed 'slice' variable is not slice type") 15 | } 16 | 17 | sliceValue := reflect.ValueOf(slice) 18 | if sliceValue.Len() == 0 { 19 | return slice 20 | } 21 | 22 | return sliceValue.Slice(1, sliceValue.Len()).Interface() 23 | } 24 | -------------------------------------------------------------------------------- /slices/tail_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TTail struct { 13 | name string 14 | arr interface{} 15 | want interface{} 16 | } 17 | 18 | var tTailBenchs = []TTail{ 19 | { 20 | name: "10", 21 | arr: []int{}, 22 | }, 23 | { 24 | name: "100", 25 | arr: []int{}, 26 | }, 27 | { 28 | name: "1000", 29 | arr: []int{}, 30 | }, 31 | { 32 | name: "10000", 33 | arr: []int{}, 34 | }, 35 | { 36 | name: "100000", 37 | arr: []int{}, 38 | }, 39 | } 40 | 41 | func init() { 42 | for j := 0; j < len(tTailBenchs); j++ { 43 | length, _ := strconv.Atoi(tTailBenchs[j].name) 44 | for i := 0; i < length/10; i++ { 45 | tTailBenchs[j].arr = append(tTailBenchs[j].arr.([]int), 0, 1, 2, 3, 4, 5, 6, 7, 8, 9) 46 | } 47 | } 48 | } 49 | 50 | func TestTail(t *testing.T) { 51 | var tests = []TTail{ 52 | { 53 | name: "nil", 54 | arr: nil, 55 | want: nil, 56 | }, 57 | { 58 | name: "empty", 59 | arr: []int{}, 60 | want: []int{}, 61 | }, 62 | { 63 | name: "just one item", 64 | arr: []int{0}, 65 | want: []int{}, 66 | }, 67 | { 68 | name: "normal-1", 69 | arr: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 70 | want: []int{1, 2, 3, 4, 5, 6, 7, 8, 9}, 71 | }, 72 | { 73 | name: "normal-2", 74 | arr: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, 75 | want: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, 76 | }, 77 | { 78 | name: "new type", 79 | arr: []string{"a", "b", "c"}, 80 | want: []string{"b", "c"}, 81 | }, 82 | } 83 | 84 | for _, subject := range tests { 85 | t.Run(subject.name, func(t *testing.T) { 86 | defer internal.DeferTestCases(t, subject.want) 87 | got := Tail(subject.arr) 88 | 89 | if !generals.Same(got, subject.want) { 90 | t.Errorf("got = %v, wanted = %v", got, subject.want) 91 | return 92 | } 93 | }) 94 | } 95 | } 96 | 97 | func BenchmarkTail(b *testing.B) { 98 | for j := 0; j < len(tTailBenchs); j++ { 99 | b.Run(fmt.Sprintf("slice_size_%s", tTailBenchs[j].name), func(b *testing.B) { 100 | for i := 0; i < b.N; i++ { 101 | Tail(tTailBenchs[j].arr) 102 | } 103 | }) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /slices/take.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Creates a sub slice of a given slice with (n) elements taken from the beginning. 10 | // 11 | // Complexity: O(1) 12 | func Take(slice interface{}, number int) interface{} { 13 | if !internal.SliceCheck(slice) { 14 | panic("passed 'slice' variable is not slice type") 15 | } 16 | 17 | values := reflect.ValueOf(slice) 18 | if values.Len() == 0 { 19 | return slice 20 | } 21 | if number > values.Len() || number < 0 { 22 | panic("'number' should be in range of slice's length") 23 | } 24 | 25 | return values.Slice(0, number).Interface() 26 | } 27 | -------------------------------------------------------------------------------- /slices/take_right.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Returns a sub slice of a given slice with (n) elements taken from the end. 10 | // 11 | // Complexity: O(1) 12 | func TakeRight(slice interface{}, number int) interface{} { 13 | if !internal.SliceCheck(slice) { 14 | panic("passed 'slice' variable is not slice type") 15 | } 16 | 17 | values := reflect.ValueOf(slice) 18 | if values.Len() == 0 { 19 | return slice 20 | } 21 | if number > values.Len() || number < 0 { 22 | panic("'number' should be in range of slice's length") 23 | } 24 | 25 | return values.Slice(values.Len()-number, values.Len()).Interface() 26 | } 27 | -------------------------------------------------------------------------------- /slices/take_right_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TTakeRight struct { 13 | name string 14 | arg1 interface{} 15 | arg2 int 16 | expected interface{} 17 | } 18 | 19 | var TTakeRightBenchs = []TTakeRight{ 20 | { 21 | name: "10", 22 | arg1: []int{}, 23 | arg2: 10, 24 | }, 25 | { 26 | name: "100", 27 | arg1: []int{}, 28 | arg2: 100, 29 | }, 30 | { 31 | name: "1000", 32 | arg1: []int{}, 33 | arg2: 1000, 34 | }, 35 | { 36 | name: "10000", 37 | arg1: []int{}, 38 | arg2: 10000, 39 | }, 40 | { 41 | name: "100000", 42 | arg1: []int{}, 43 | arg2: 100000, 44 | }, 45 | } 46 | 47 | func init() { 48 | for i := 0; i < len(TTakeRightBenchs); i++ { 49 | k, _ := strconv.Atoi(TTakeRightBenchs[i].name) 50 | for j := 0; j < k/10; j++ { 51 | TTakeRightBenchs[i].arg1 = append(TTakeRightBenchs[i].arg1.([]int), 1, 2, 3, 4, 5, 6, 7, 8, 9, 0) 52 | } 53 | } 54 | } 55 | 56 | func TestTakeRight(t *testing.T) { 57 | var tests = []TTakeRight{ 58 | { 59 | name: "nil", 60 | arg1: nil, 61 | arg2: 2, 62 | expected: nil, 63 | }, 64 | { 65 | name: "empty", 66 | arg1: []int{}, 67 | arg2: 4, 68 | expected: []int{}, 69 | }, 70 | { 71 | name: "empty", 72 | arg1: []int{1, 2, 3, 4}, 73 | arg2: 0, 74 | expected: []int{}, 75 | }, 76 | { 77 | name: "default", 78 | arg1: []int{2, 4, 6, 8, 0, 1, 3, 5, 7, 9}, 79 | arg2: 5, 80 | expected: []int{1, 3, 5, 7, 9}, 81 | }, 82 | { 83 | name: "default1", 84 | arg1: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, 85 | arg2: 7, 86 | expected: []int{4, 5, 6, 7, 8, 9, 0}, 87 | }, 88 | } 89 | 90 | for _, sample := range tests { 91 | t.Run(sample.name, func(t *testing.T) { 92 | defer internal.DeferTestCases(t, sample.expected) 93 | got := TakeRight(sample.arg1, sample.arg2) 94 | 95 | if !generals.Same(got, sample.expected) { 96 | t.Errorf("got = %v, wanted = %v", got, sample.expected) 97 | return 98 | } 99 | }) 100 | } 101 | } 102 | 103 | func BenchmarkTakeRight(b *testing.B) { 104 | for _, sample := range TTakeRightBenchs { 105 | b.Run(fmt.Sprintf("input_size_%s", sample.name), func(b *testing.B) { 106 | for i := 0; i < b.N; i++ { 107 | TakeRight(sample.arg1, sample.arg2) 108 | } 109 | }) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /slices/take_right_while.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Creates a sub slice of a slice with elements taken from the end. 10 | // Elements are taken until passed function returns false. 11 | // 12 | // example for 'function': 13 | // 14 | // func isEven(input interface{}) bool { 15 | // return input.(int) % 2 == 0 16 | // } 17 | // 18 | // Complexity: O(n) 19 | // 20 | // n = number of elements that passed function returns true on them 21 | func TakeRightWhile(slice interface{}, function func(interface{}) bool) interface{} { 22 | if !internal.SliceCheck(slice) { 23 | panic("passed 'slice' variable is not slice type") 24 | } 25 | 26 | sliceValue := reflect.ValueOf(slice) 27 | i := 0 28 | for i = sliceValue.Len() - 1; i > -1; i-- { 29 | if !function(sliceValue.Index(i).Interface()) { 30 | break 31 | } 32 | } 33 | 34 | return sliceValue.Slice(0, i+1).Interface() 35 | } 36 | -------------------------------------------------------------------------------- /slices/take_right_while_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TTakeRightWhile struct { 13 | name string 14 | arg1 interface{} 15 | expected interface{} 16 | } 17 | 18 | var TTakeRightWhileBenchs = []TTakeRightWhile{ 19 | { 20 | name: "10", 21 | arg1: []int{}, 22 | }, 23 | { 24 | name: "100", 25 | arg1: []int{}, 26 | }, 27 | { 28 | name: "1000", 29 | arg1: []int{}, 30 | }, 31 | { 32 | name: "10000", 33 | arg1: []int{}, 34 | }, 35 | { 36 | name: "100000", 37 | arg1: []int{}, 38 | }, 39 | } 40 | 41 | func init() { 42 | for i := 0; i < len(TTakeRightWhileBenchs); i++ { 43 | k, _ := strconv.Atoi(TTakeRightWhileBenchs[i].name) 44 | for j := 0; j < k/10; j++ { 45 | TTakeRightWhileBenchs[i].arg1 = append(TTakeRightWhileBenchs[i].arg1.([]int), 10, 10, 20, 30, 40, 50, 60, 70, 80, 90) 46 | } 47 | } 48 | } 49 | 50 | func compareTakeRightWhileTest(input interface{}) bool { 51 | return input.(int) > 6 52 | } 53 | 54 | func TestTakeRightWhile(t *testing.T) { 55 | var tests = []TTakeRightWhile{ 56 | { 57 | name: "nil", 58 | arg1: nil, 59 | expected: nil, 60 | }, 61 | { 62 | name: "empty", 63 | arg1: []int{}, 64 | expected: []int{}, 65 | }, 66 | { 67 | name: "default", 68 | arg1: []int{1, 2, 3, 4, 5, 6, 7, 8, 9}, 69 | expected: []int{1, 2, 3, 4, 5, 6}, 70 | }, 71 | } 72 | for _, sample := range tests { 73 | t.Run(sample.name, func(t *testing.T) { 74 | defer internal.DeferTestCases(t, sample.expected) 75 | got := TakeRightWhile(sample.arg1, compareTakeRightWhileTest) 76 | 77 | if !generals.Same(got, sample.expected) { 78 | t.Errorf("got = %v, wanted = %v", got, sample.expected) 79 | return 80 | } 81 | }) 82 | } 83 | } 84 | 85 | func BenchmarkTakeRightWhile(b *testing.B) { 86 | for _, sample := range TTakeRightWhileBenchs { 87 | b.Run(fmt.Sprintf("input_size_%s", sample.name), func(b *testing.B) { 88 | for i := 0; i < b.N; i++ { 89 | TakeRightWhile(sample.arg1, compareTakeRightWhileTest) 90 | } 91 | }) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /slices/take_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TTake struct { 13 | name string 14 | arg1 interface{} 15 | arg2 int 16 | expected interface{} 17 | } 18 | 19 | var TTakeBenchs = []TTake{ 20 | { 21 | name: "10", 22 | arg1: []int{}, 23 | arg2: 10, 24 | }, 25 | { 26 | name: "100", 27 | arg1: []int{}, 28 | arg2: 100, 29 | }, 30 | { 31 | name: "1000", 32 | arg1: []int{}, 33 | arg2: 1000, 34 | }, 35 | { 36 | name: "10000", 37 | arg1: []int{}, 38 | arg2: 10000, 39 | }, 40 | { 41 | name: "100000", 42 | arg1: []int{}, 43 | arg2: 100000, 44 | }, 45 | } 46 | 47 | func init() { 48 | for i := 0; i < len(TTakeBenchs); i++ { 49 | k, _ := strconv.Atoi(TTakeBenchs[i].name) 50 | for j := 0; j < k/10; j++ { 51 | TTakeBenchs[i].arg1 = append(TTakeBenchs[i].arg1.([]int), 1, 2, 3, 4, 5, 6, 7, 8, 9, 0) 52 | } 53 | } 54 | } 55 | 56 | func TestTake(t *testing.T) { 57 | var tests = []TTake{ 58 | { 59 | name: "nil", 60 | arg1: nil, 61 | arg2: 2, 62 | expected: nil, 63 | }, 64 | { 65 | name: "empty", 66 | arg1: []int{}, 67 | arg2: 4, 68 | expected: []int{}, 69 | }, 70 | { 71 | name: "empty1", 72 | arg1: []int{1, 2, 3, 4}, 73 | arg2: 0, 74 | expected: []int{}, 75 | }, 76 | { 77 | name: "default", 78 | arg1: []int{2, 4, 6, 8, 0, 1, 3, 5, 7, 9}, 79 | arg2: 5, 80 | expected: []int{2, 4, 6, 8, 0}, 81 | }, 82 | { 83 | name: "default1", 84 | arg1: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, 85 | arg2: 7, 86 | expected: []int{1, 2, 3, 4, 5, 6, 7}, 87 | }, 88 | { 89 | name: "new type", 90 | arg1: []string{"a", "b", "c"}, 91 | arg2: 1, 92 | expected: []string{"a"}, 93 | }, 94 | } 95 | 96 | for _, sample := range tests { 97 | t.Run(sample.name, func(t *testing.T) { 98 | defer internal.DeferTestCases(t, sample.expected) 99 | got := Take(sample.arg1, sample.arg2) 100 | 101 | if !generals.Same(got, sample.expected) { 102 | t.Errorf("got = %v, wanted = %v", got, sample.expected) 103 | return 104 | } 105 | }) 106 | } 107 | } 108 | 109 | func BenchmarkTake(b *testing.B) { 110 | for _, sample := range TTakeBenchs { 111 | b.Run(fmt.Sprintf("input_size_%s", sample.name), func(b *testing.B) { 112 | for i := 0; i < b.N; i++ { 113 | Take(sample.arg1, sample.arg2) 114 | } 115 | }) 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /slices/take_while.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Creates a sub slice of a slice with elements taken from the beginning. 10 | // Elements are taken until passed function returns false. 11 | // 12 | // Complexity: O(n) 13 | // 14 | // n = number of elements that passed function returns true on them 15 | func TakeWhile(slice interface{}, function func(interface{}) bool) interface{} { 16 | if !internal.SliceCheck(slice) { 17 | panic("passed 'slice' variable is not slice type") 18 | } 19 | 20 | sliceValue := reflect.ValueOf(slice) 21 | i := 0 22 | for i = 0; i < sliceValue.Len(); i++ { 23 | item := sliceValue.Index(i).Interface() 24 | if !function(item) { 25 | break 26 | } 27 | } 28 | 29 | return sliceValue.Slice(i, sliceValue.Len()).Interface() 30 | } 31 | -------------------------------------------------------------------------------- /slices/take_while_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TTakeWhile struct { 13 | name string 14 | arg1 interface{} 15 | expected interface{} 16 | } 17 | 18 | var TTakeWhileBenchs = []TTakeWhile{ 19 | { 20 | name: "10", 21 | arg1: []int{}, 22 | }, 23 | { 24 | name: "100", 25 | arg1: []int{}, 26 | }, 27 | { 28 | name: "1000", 29 | arg1: []int{}, 30 | }, 31 | { 32 | name: "10000", 33 | arg1: []int{}, 34 | }, 35 | { 36 | name: "100000", 37 | arg1: []int{}, 38 | }, 39 | } 40 | 41 | func init() { 42 | for i := 0; i < len(TTakeWhileBenchs); i++ { 43 | k, _ := strconv.Atoi(TTakeWhileBenchs[i].name) 44 | for j := 0; j < k/10; j++ { 45 | TTakeWhileBenchs[i].arg1 = append(TTakeWhileBenchs[i].arg1.([]int), []int{0, -1, -2, -3, -4, -5, -6, -7, -8, -9}...) 46 | } 47 | } 48 | } 49 | 50 | func compareTakeWhileTest(input interface{}) bool { 51 | return input.(int) < 6 52 | } 53 | 54 | func TestTakeWhile(t *testing.T) { 55 | var tests = []TTakeWhile{ 56 | { 57 | name: "nil", 58 | arg1: nil, 59 | expected: nil, 60 | }, 61 | { 62 | name: "empty", 63 | arg1: []int{}, 64 | expected: []int{}, 65 | }, 66 | { 67 | name: "default", 68 | arg1: []int{1, 2, 3, 4, 5, 6, 7, 8, 9}, 69 | expected: []int{6, 7, 8, 9}, 70 | }, 71 | } 72 | for _, sample := range tests { 73 | t.Run(sample.name, func(t *testing.T) { 74 | defer internal.DeferTestCases(t, sample.expected) 75 | got := TakeWhile(sample.arg1, compareTakeWhileTest) 76 | 77 | if !generals.Same(got, sample.expected) { 78 | t.Errorf("got = %v, wanted = %v", got, sample.expected) 79 | return 80 | } 81 | }) 82 | } 83 | } 84 | 85 | func BenchmarkTakeWhile(b *testing.B) { 86 | for _, sample := range TTakeWhileBenchs { 87 | b.Run(fmt.Sprintf("input_size_%s", sample.name), func(b *testing.B) { 88 | for i := 0; i < b.N; i++ { 89 | TakeWhile(sample.arg1, compareTakeWhileTest) 90 | } 91 | }) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /slices/union.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Returns a slice of unique values, in order, from combine of all given slices. 10 | // 11 | // Complexity: O(n) 12 | // 13 | // n = length of both slices combined 14 | func Union(slice1, slice2 interface{}) interface{} { 15 | if !internal.SliceCheck(slice1) { 16 | panic("passed 'slice1' variable is not slice type") 17 | } 18 | if !internal.SliceCheck(slice2) { 19 | panic("passed 'slice2' variable is not slice type") 20 | } 21 | 22 | sameType := false 23 | values := reflect.ValueOf(slice1) 24 | values2 := reflect.ValueOf(slice2) 25 | if values.Type().String() == values2.Type().String() { 26 | sameType = true 27 | } 28 | 29 | var union reflect.Value = reflect.Value{} 30 | if sameType { 31 | union = reflect.MakeSlice(values.Type(), 0, values.Len()+values2.Len()) 32 | } else { 33 | union = reflect.MakeSlice(reflect.TypeOf([]interface{}{}), 0, values.Len()+values2.Len()) 34 | } 35 | 36 | m := map[interface{}]bool{} 37 | for i := 0; i < values.Len(); i++ { 38 | value := values.Index(i).Interface() 39 | if _, ok := m[value]; !ok { 40 | m[value] = true 41 | union = reflect.Append(union, reflect.ValueOf(value)) 42 | } 43 | } 44 | for i := 0; i < values2.Len(); i++ { 45 | value := values2.Index(i).Interface() 46 | if _, ok := m[value]; !ok { 47 | m[value] = true 48 | union = reflect.Append(union, reflect.ValueOf(value)) 49 | } 50 | } 51 | 52 | return union.Interface() 53 | } 54 | -------------------------------------------------------------------------------- /slices/union_by.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Returns a slice of unique values, in order, from combine of all given slices. 10 | // 11 | // example for 'function': 12 | // 13 | // func makeInt(input interface{}) interface{} { 14 | // return int(input.(float64)) 15 | // } 16 | // 17 | // Complexity: O(n) 18 | // 19 | // n = length of both slices combined 20 | func UnionBy(slice1, slice2 interface{}, function func(interface{}) interface{}) interface{} { 21 | if !internal.SliceCheck(slice1) { 22 | panic("passed 'slice1' variable is not slice type") 23 | } 24 | if !internal.SliceCheck(slice2) { 25 | panic("passed 'slice2' variable is not slice type") 26 | } 27 | 28 | sameType := false 29 | values := reflect.ValueOf(slice1) 30 | values2 := reflect.ValueOf(slice2) 31 | if values.Type().String() == values2.Type().String() { 32 | sameType = true 33 | } 34 | 35 | var union reflect.Value = reflect.Value{} 36 | if sameType { 37 | union = reflect.MakeSlice(values.Type(), 0, values.Len()+values2.Len()) 38 | } else { 39 | union = reflect.MakeSlice(reflect.TypeOf([]interface{}{}), 0, values.Len()+values2.Len()) 40 | } 41 | 42 | m := map[interface{}]bool{} 43 | for i := 0; i < values.Len(); i++ { 44 | value := function(values.Index(i).Interface()) 45 | if _, ok := m[value]; !ok { 46 | m[value] = true 47 | union = reflect.Append(union, values.Index(i)) 48 | } 49 | } 50 | for i := 0; i < values2.Len(); i++ { 51 | value := function(values2.Index(i).Interface()) 52 | if _, ok := m[value]; !ok { 53 | m[value] = true 54 | union = reflect.Append(union, values2.Index(i)) 55 | } 56 | } 57 | 58 | return union.Interface() 59 | } 60 | -------------------------------------------------------------------------------- /slices/unique.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Creates a duplicate-free version of a slice that only 10 | // keeps the first occurrence of each element. 11 | // 12 | // The order of result values is determined by the order 13 | // they occur in the slice. 14 | // 15 | // Complexity: O(n) 16 | func Unique(slice interface{}) interface{} { 17 | if !internal.SliceCheck(slice) { 18 | panic("passed 'slice' variable is not slice type") 19 | } 20 | 21 | sliceValue := reflect.ValueOf(slice) 22 | outputValue := reflect.MakeSlice(reflect.TypeOf(slice), 0, sliceValue.Len()) 23 | m := map[interface{}]bool{} 24 | for i := 0; i < sliceValue.Len(); i++ { 25 | item := sliceValue.Index(i).Interface() 26 | if _, ok := m[item]; !ok { 27 | m[item] = true 28 | outputValue = reflect.Append(outputValue, reflect.ValueOf(item)) 29 | } 30 | } 31 | 32 | return outputValue.Interface() 33 | } 34 | -------------------------------------------------------------------------------- /slices/unique_by.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Creates a duplicate-free version of a slice that only 10 | // keeps the first occurrence of each element. 11 | // 12 | // The order of result values is determined by the order 13 | // they occur in the slice. 14 | // 15 | // example for 'function': 16 | // 17 | // func makeInt(input interface{}) interface{} { 18 | // return int(input.(float64)) 19 | // } 20 | // 21 | // Complexity: O(n) 22 | func UniqueBy(slice interface{}, function func(interface{}) interface{}) interface{} { 23 | if !internal.SliceCheck(slice) { 24 | panic("passed 'slice' variable is not slice type") 25 | } 26 | 27 | sliceValue := reflect.ValueOf(slice) 28 | outputValue := reflect.MakeSlice(reflect.TypeOf(slice), 0, sliceValue.Len()) 29 | m := map[interface{}]bool{} 30 | for i := 0; i < sliceValue.Len(); i++ { 31 | item := sliceValue.Index(i).Interface() 32 | compareItem := function(item) 33 | if _, ok := m[compareItem]; !ok { 34 | m[compareItem] = true 35 | outputValue = reflect.Append(outputValue, reflect.ValueOf(item)) 36 | } 37 | } 38 | 39 | return outputValue.Interface() 40 | } 41 | -------------------------------------------------------------------------------- /slices/unique_by_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TUniqueBy struct { 13 | name string 14 | arg interface{} 15 | expected interface{} 16 | } 17 | 18 | var TUniqueByBenchs = []TUniqueBy{ 19 | { 20 | name: "10", 21 | arg: []int{}, 22 | }, 23 | { 24 | name: "100", 25 | arg: []int{}, 26 | }, 27 | { 28 | name: "1000", 29 | arg: []int{}, 30 | }, 31 | { 32 | name: "10000", 33 | arg: []int{}, 34 | }, 35 | { 36 | name: "100000", 37 | arg: []int{}, 38 | }, 39 | } 40 | 41 | func init() { 42 | for i := 0; i < len(TUniqueByBenchs); i++ { 43 | k, _ := strconv.Atoi(TUniqueByBenchs[i].name) 44 | for j := 0; j < k/10; j++ { 45 | TUniqueByBenchs[i].arg = append(TUniqueByBenchs[i].arg.([]int), []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}...) 46 | } 47 | } 48 | } 49 | 50 | func compareItemUniqueByTest(input interface{}) interface{} { 51 | return input 52 | } 53 | 54 | func TestUniqueBy(t *testing.T) { 55 | var tests = []TUniqueBy{ 56 | { 57 | name: "nil", 58 | arg: nil, 59 | expected: nil, 60 | }, 61 | { 62 | name: "empty", 63 | arg: []int{}, 64 | expected: []int{}, 65 | }, 66 | { 67 | name: "normal", 68 | arg: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 69 | expected: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 70 | }, 71 | { 72 | name: "duplicate", 73 | arg: []int{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9}, 74 | expected: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 75 | }, 76 | } 77 | for _, sample := range tests { 78 | t.Run(sample.name, func(t *testing.T) { 79 | defer internal.DeferTestCases(t, sample.expected) 80 | got := UniqueBy(sample.arg, compareItemUniqueByTest) 81 | 82 | if !generals.Same(got, sample.expected) { 83 | t.Errorf("got = %v, wanted = %v", got, sample.expected) 84 | return 85 | } 86 | }) 87 | } 88 | } 89 | 90 | func BenchmarkUniqueBy(b *testing.B) { 91 | for _, sample := range TUniqueByBenchs { 92 | b.Run(fmt.Sprintf("input_size_%s", sample.name), func(b *testing.B) { 93 | for i := 0; i < b.N; i++ { 94 | UniqueBy(sample.arg, compareItemUniqueByTest) 95 | } 96 | }) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /slices/unique_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TUnique struct { 13 | name string 14 | arg interface{} 15 | expected interface{} 16 | } 17 | 18 | var TUniqueBenchs = []TUnique{ 19 | { 20 | name: "10", 21 | arg: []int{}, 22 | }, 23 | { 24 | name: "100", 25 | arg: []int{}, 26 | }, 27 | { 28 | name: "1000", 29 | arg: []int{}, 30 | }, 31 | { 32 | name: "10000", 33 | arg: []int{}, 34 | }, 35 | { 36 | name: "100000", 37 | arg: []int{}, 38 | }, 39 | } 40 | 41 | func init() { 42 | for i := 0; i < len(TUniqueBenchs); i++ { 43 | k, _ := strconv.Atoi(TUniqueBenchs[i].name) 44 | for j := 0; j < k/10; j++ { 45 | TUniqueBenchs[i].arg = append(TUniqueBenchs[i].arg.([]int), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) 46 | } 47 | } 48 | } 49 | 50 | func TestUnique(t *testing.T) { 51 | var tests = []TUnique{ 52 | { 53 | name: "nil", 54 | arg: nil, 55 | expected: nil, 56 | }, 57 | { 58 | name: "empty", 59 | arg: []int{}, 60 | expected: []int{}, 61 | }, 62 | { 63 | name: "normal", 64 | arg: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 65 | expected: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 66 | }, 67 | { 68 | name: "duplicate", 69 | arg: []int{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9}, 70 | expected: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 71 | }, 72 | } 73 | for _, sample := range tests { 74 | t.Run(sample.name, func(t *testing.T) { 75 | defer internal.DeferTestCases(t, sample.expected) 76 | got := Unique(sample.arg) 77 | 78 | if !generals.Same(got, sample.expected) { 79 | t.Errorf("got = %v, wanted = %v", got, sample.expected) 80 | return 81 | } 82 | }) 83 | } 84 | } 85 | 86 | func BenchmarkUnique(b *testing.B) { 87 | for _, sample := range TUniqueBenchs { 88 | b.Run(fmt.Sprintf("input_size_%s", sample.name), func(b *testing.B) { 89 | for i := 0; i < b.N; i++ { 90 | Unique(sample.arg) 91 | } 92 | }) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /slices/unzip.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | 7 | "github.com/golodash/godash/internal" 8 | ) 9 | 10 | // This method is like Zip except that it accepts a slice of grouped elements 11 | // and creates a slice regrouping the elements to their pre-zip configuration. 12 | // 13 | // Complexity: O(n) 14 | func Unzip(slices interface{}) interface{} { 15 | if !internal.SliceCheck(slices) { 16 | panic("passed 'slices' variable is not slice type") 17 | } 18 | 19 | var output = reflect.Value{} 20 | sliceItemType := reflect.TypeOf(slices) 21 | slicesValue := reflect.ValueOf(slices) 22 | itItInterface := false 23 | for sliceItemType.Kind() == reflect.Slice { 24 | sliceItemType = sliceItemType.Elem() 25 | } 26 | if sliceItemType.Kind() != reflect.Interface { 27 | output = reflect.MakeSlice(reflect.TypeOf(slices), 0, slicesValue.Len()*2) 28 | } else { 29 | output = reflect.MakeSlice(reflect.TypeOf([][]interface{}{}), 0, slicesValue.Len()*2) 30 | itItInterface = true 31 | } 32 | 33 | // Check length of all slices 34 | length := -1 35 | if slicesValue.Len() != 0 { 36 | for i := 0; i < slicesValue.Len(); i++ { 37 | itemValue := reflect.ValueOf(slicesValue.Index(i).Interface()) 38 | if !internal.SliceCheck(slicesValue.Index(i).Interface()) { 39 | panic(fmt.Sprintf("item in %d index is not a slice", i)) 40 | } 41 | if length == -1 { 42 | length = itemValue.Len() 43 | } 44 | if itemValue.Len() != length { 45 | panic(fmt.Sprintf("item in %d index is not the same length with its previous item", i)) 46 | } 47 | } 48 | } else { 49 | return output.Interface() 50 | } 51 | 52 | // Actual unzip 53 | tempMap := map[string]interface{}{} 54 | for j := 0; j < length; j++ { 55 | indexMap := fmt.Sprint(j) 56 | for i := 0; i < slicesValue.Len(); i++ { 57 | innerSliceValue := reflect.ValueOf(slicesValue.Index(i).Interface()) 58 | itemValue := reflect.ValueOf(innerSliceValue.Index(j).Interface()) 59 | 60 | if _, ok := tempMap[indexMap]; !ok { 61 | if itItInterface { 62 | tempMap[indexMap] = reflect.MakeSlice(reflect.TypeOf([]interface{}{}), 0, length).Interface() 63 | } else { 64 | tempMap[indexMap] = reflect.MakeSlice(reflect.SliceOf(itemValue.Type()), 0, length).Interface() 65 | } 66 | } 67 | 68 | tempMap[indexMap] = reflect.Append(reflect.ValueOf(tempMap[indexMap]), itemValue).Interface() 69 | } 70 | } 71 | 72 | // Put outputs into slice 73 | for i := 0; i < len(tempMap); i++ { 74 | output = reflect.Append(output, reflect.ValueOf(tempMap[fmt.Sprint(i)])) 75 | } 76 | 77 | return output.Interface() 78 | } 79 | -------------------------------------------------------------------------------- /slices/without.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | // Returns a slice of 'slice' elements that are not included in the 4 | // other given slice using equality comparisons. 5 | // 6 | // Complexity: O(n*m) 7 | // 8 | // n = length of 'slice' 9 | // 10 | // m = length of 'notIncluded' 11 | func Without(slice, notIncluded interface{}) interface{} { 12 | return Difference(slice, notIncluded) 13 | } 14 | -------------------------------------------------------------------------------- /slices/xor.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Returns a slice of unique values that is the symmetric difference of the given slices. 10 | // 11 | // Complexity: O(n) 12 | // 13 | // n = number of all elements in both 'slice1' and 'slice2' 14 | func Xor(slice1, slice2 interface{}) interface{} { 15 | if !internal.SliceCheck(slice1) { 16 | panic("passed 'slice1' variable is not slice type") 17 | } 18 | if !internal.SliceCheck(slice2) { 19 | panic("passed 'slice2' variable is not slice type") 20 | } 21 | 22 | slice1Value := reflect.ValueOf(slice1) 23 | slice2Value := reflect.ValueOf(slice2) 24 | length := slice1Value.Len() + slice2Value.Len() 25 | var oneTimeSeenItems = reflect.Value{} 26 | if slice1Value.Type().String() == slice2Value.Type().String() { 27 | oneTimeSeenItems = reflect.MakeSlice(slice1Value.Type(), 0, length) 28 | } else { 29 | oneTimeSeenItems = reflect.MakeSlice(reflect.TypeOf([]interface{}{}), 0, length) 30 | } 31 | 32 | // find which items repeated more than once 33 | seenMap := map[interface{}]bool{} 34 | RepeatMap := map[interface{}]bool{} 35 | for i := 0; i < slice1Value.Len(); i++ { 36 | item := slice1Value.Index(i).Interface() 37 | if _, ok := seenMap[item]; !ok { 38 | seenMap[item] = true 39 | } else { 40 | RepeatMap[item] = true 41 | } 42 | } 43 | for i := 0; i < slice2Value.Len(); i++ { 44 | item := slice2Value.Index(i).Interface() 45 | if _, ok := seenMap[item]; !ok { 46 | seenMap[item] = true 47 | } else { 48 | RepeatMap[item] = true 49 | } 50 | } 51 | 52 | // add all items except items which appeared more than once 53 | for i := 0; i < slice1Value.Len(); i++ { 54 | item := slice1Value.Index(i).Interface() 55 | if _, ok := RepeatMap[item]; !ok { 56 | oneTimeSeenItems = reflect.Append(oneTimeSeenItems, reflect.ValueOf(item)) 57 | } 58 | } 59 | for i := 0; i < slice2Value.Len(); i++ { 60 | item := slice2Value.Index(i).Interface() 61 | if _, ok := RepeatMap[item]; !ok { 62 | oneTimeSeenItems = reflect.Append(oneTimeSeenItems, reflect.ValueOf(item)) 63 | } 64 | } 65 | 66 | return oneTimeSeenItems.Interface() 67 | } 68 | -------------------------------------------------------------------------------- /slices/xor_by.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // This method is like Xor except that it accepts a function which is 10 | // invoked for each element of each slices for comparing them. 11 | // 12 | // example for 'function': 13 | // 14 | // func makeInt(input interface{}) interface{} { 15 | // return int(input.(float64)) 16 | // } 17 | // 18 | // Complexity: O(n) 19 | // 20 | // n = number of all elements in both 'slice1' and 'slice2' 21 | func XorBy(slice1, slice2 interface{}, function func(interface{}) interface{}) interface{} { 22 | if !internal.SliceCheck(slice1) { 23 | panic("passed 'slice1' variable is not slice type") 24 | } 25 | if !internal.SliceCheck(slice2) { 26 | panic("passed 'slice2' variable is not slice type") 27 | } 28 | 29 | slice1Value := reflect.ValueOf(slice1) 30 | slice2Value := reflect.ValueOf(slice2) 31 | length := slice1Value.Len() + slice2Value.Len() 32 | var oneTimeSeenItems = reflect.Value{} 33 | if slice1Value.Type().String() == slice2Value.Type().String() { 34 | oneTimeSeenItems = reflect.MakeSlice(slice1Value.Type(), 0, length) 35 | } else { 36 | oneTimeSeenItems = reflect.MakeSlice(reflect.TypeOf([]interface{}{}), 0, length) 37 | } 38 | 39 | // find which items repeated more than once 40 | seenMap := map[interface{}]bool{} 41 | RepeatMap := map[interface{}]bool{} 42 | for i := 0; i < slice1Value.Len(); i++ { 43 | item := slice1Value.Index(i).Interface() 44 | compare := function(item) 45 | if _, ok := seenMap[compare]; !ok { 46 | seenMap[compare] = true 47 | } else { 48 | RepeatMap[compare] = true 49 | } 50 | } 51 | for i := 0; i < slice2Value.Len(); i++ { 52 | item := slice2Value.Index(i).Interface() 53 | compare := function(item) 54 | if _, ok := seenMap[compare]; !ok { 55 | seenMap[compare] = true 56 | } else { 57 | RepeatMap[compare] = true 58 | } 59 | } 60 | 61 | // add all items except items which appeared more than once 62 | for i := 0; i < slice1Value.Len(); i++ { 63 | item := slice1Value.Index(i).Interface() 64 | compare := function(item) 65 | if _, ok := RepeatMap[compare]; !ok { 66 | oneTimeSeenItems = reflect.Append(oneTimeSeenItems, reflect.ValueOf(item)) 67 | } 68 | } 69 | for i := 0; i < slice2Value.Len(); i++ { 70 | item := slice2Value.Index(i).Interface() 71 | compare := function(item) 72 | if _, ok := RepeatMap[compare]; !ok { 73 | oneTimeSeenItems = reflect.Append(oneTimeSeenItems, reflect.ValueOf(item)) 74 | } 75 | } 76 | 77 | return oneTimeSeenItems.Interface() 78 | } 79 | -------------------------------------------------------------------------------- /slices/xor_by_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TXorBy struct { 13 | name string 14 | arg1 interface{} 15 | arg2 interface{} 16 | expected interface{} 17 | } 18 | 19 | var TXorByBenchs = []TXorBy{ 20 | { 21 | name: "10", 22 | arg1: []interface{}{}, 23 | arg2: []interface{}{}, 24 | }, 25 | { 26 | name: "100", 27 | arg1: []interface{}{}, 28 | arg2: []interface{}{}, 29 | }, 30 | { 31 | name: "1000", 32 | arg1: []interface{}{}, 33 | arg2: []interface{}{}, 34 | }, 35 | { 36 | name: "10000", 37 | arg1: []interface{}{}, 38 | arg2: []interface{}{}, 39 | }, 40 | { 41 | name: "100000", 42 | arg1: []interface{}{}, 43 | arg2: []interface{}{}, 44 | }, 45 | } 46 | 47 | func init() { 48 | for i := 0; i < len(TXorByBenchs); i++ { 49 | k, _ := strconv.Atoi(TXorByBenchs[i].name) 50 | for j := 0; j < k/10; j++ { 51 | TXorByBenchs[i].arg1 = append(TXorByBenchs[i].arg1.([]interface{}), 0, 1, 2, 3, 4, 5, 6, 7, 8, 9) 52 | TXorByBenchs[i].arg2 = append(TXorByBenchs[i].arg2.([]interface{}), 0, 1, 2, 3, 4, 5, 6, 7, 8, 9) 53 | } 54 | } 55 | } 56 | 57 | func compareXorByTest(value1 interface{}) interface{} { 58 | return value1 59 | } 60 | 61 | func TestXorBy(t *testing.T) { 62 | var tests = []TXorBy{ 63 | { 64 | name: "nil", 65 | arg1: nil, 66 | arg2: nil, 67 | expected: nil, 68 | }, 69 | { 70 | name: "empty", 71 | arg1: []interface{}{}, 72 | arg2: []interface{}{}, 73 | expected: []interface{}{}, 74 | }, 75 | { 76 | name: "normal", 77 | arg1: []int{1, 2}, 78 | arg2: []int{3, 4, 1}, 79 | expected: []int{2, 3, 4}, 80 | }, 81 | } 82 | for _, sample := range tests { 83 | t.Run(sample.name, func(t *testing.T) { 84 | defer internal.DeferTestCases(t, sample.expected) 85 | got := XorBy(sample.arg1, sample.arg2, compareXorByTest) 86 | 87 | if !generals.Same(got, sample.expected) { 88 | t.Errorf("got = %v, wanted = %v", got, sample.expected) 89 | return 90 | } 91 | }) 92 | } 93 | } 94 | 95 | func BenchmarkXorBy(b *testing.B) { 96 | for _, sample := range TXorByBenchs { 97 | b.Run(fmt.Sprintf("input_size_%s", sample.name), func(b *testing.B) { 98 | for i := 0; i < b.N; i++ { 99 | XorBy(sample.arg1, sample.arg2, compareXorByTest) 100 | } 101 | }) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /slices/xor_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TXor struct { 13 | name string 14 | arg1 interface{} 15 | arg2 interface{} 16 | expected interface{} 17 | } 18 | 19 | var TXorBenchs = []TXor{ 20 | { 21 | name: "10", 22 | arg1: []interface{}{}, 23 | arg2: []interface{}{}, 24 | }, 25 | { 26 | name: "100", 27 | arg1: []interface{}{}, 28 | arg2: []interface{}{}, 29 | }, 30 | { 31 | name: "1000", 32 | arg1: []interface{}{}, 33 | arg2: []interface{}{}, 34 | }, 35 | { 36 | name: "10000", 37 | arg1: []interface{}{}, 38 | arg2: []interface{}{}, 39 | }, 40 | { 41 | name: "100000", 42 | arg1: []interface{}{}, 43 | arg2: []interface{}{}, 44 | }, 45 | } 46 | 47 | func init() { 48 | for i := 0; i < len(TXorBenchs); i++ { 49 | k, _ := strconv.Atoi(TXorBenchs[i].name) 50 | for j := 0; j < k/10; j++ { 51 | TXorBenchs[i].arg1 = append(TXorBenchs[i].arg1.([]interface{}), 0, 1, 2, 3, 4, 5, 6, 7, 8, 9) 52 | TXorBenchs[i].arg2 = append(TXorBenchs[i].arg2.([]interface{}), 0, 1, 2, 3, 4, 5, 6, 7, 8, 9) 53 | } 54 | } 55 | } 56 | 57 | func TestXor(t *testing.T) { 58 | var tests = []TXor{ 59 | { 60 | name: "nil", 61 | arg1: nil, 62 | arg2: nil, 63 | expected: nil, 64 | }, 65 | { 66 | name: "empty", 67 | arg1: []interface{}{}, 68 | arg2: []interface{}{}, 69 | expected: []interface{}{}, 70 | }, 71 | { 72 | name: "normal", 73 | arg1: []int{1, 2}, 74 | arg2: []int{1, 4}, 75 | expected: []int{2, 4}, 76 | }, 77 | { 78 | name: "type based", 79 | arg1: []string{"1", "2"}, 80 | arg2: []int{1, 4}, 81 | expected: []interface{}{"1", "2", 1, 4}, 82 | }, 83 | } 84 | for _, sample := range tests { 85 | t.Run(sample.name, func(t *testing.T) { 86 | defer internal.DeferTestCases(t, sample.expected) 87 | got := Xor(sample.arg1, sample.arg2) 88 | 89 | if !generals.Same(got, sample.expected) { 90 | t.Errorf("got = %v, wanted = %v", got, sample.expected) 91 | return 92 | } 93 | }) 94 | } 95 | } 96 | 97 | func BenchmarkXor(b *testing.B) { 98 | for _, sample := range TXorBenchs { 99 | b.Run(fmt.Sprintf("input_size_%s", sample.name), func(b *testing.B) { 100 | for i := 0; i < b.N; i++ { 101 | Xor(sample.arg1, sample.arg2) 102 | } 103 | }) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /slices/zip_by.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | 7 | "github.com/golodash/godash/internal" 8 | ) 9 | 10 | // This method is like Zip except that it accepts a function to specify 11 | // how grouped values should be combined. 12 | // 13 | // example for 'function': 14 | // 15 | // func returnZipByTest(inputs interface{}) interface{} { 16 | // output := 0 17 | // literalInputs := inputs.([]int) 18 | // for i := 0; i < len(literalInputs); i++ { 19 | // output += literalInputs[i] 20 | // } 21 | // return output 22 | // } 23 | // 24 | // Complexity: O(n) 25 | func ZipBy(slices interface{}, function func(interface{}) interface{}) interface{} { 26 | if !internal.SliceCheck(slices) { 27 | panic("passed 'slices' variable is not slice type") 28 | } 29 | 30 | // Calculating output type 31 | var output = reflect.Value{} 32 | sliceItemType := reflect.TypeOf(slices) 33 | slicesValue := reflect.ValueOf(slices) 34 | for sliceItemType.Kind() == reflect.Slice { 35 | sliceItemType = sliceItemType.Elem() 36 | } 37 | if sliceItemType.Kind() != reflect.Interface { 38 | output = reflect.MakeSlice(reflect.TypeOf(slices).Elem(), 0, slicesValue.Len()/2) 39 | } else { 40 | output = reflect.MakeSlice(reflect.TypeOf([][]interface{}{}), 0, slicesValue.Len()/2) 41 | } 42 | 43 | // Calculating length 44 | sizeOfAllSlices := 0 45 | maxLength := -1 46 | for i := 0; i < slicesValue.Len(); i++ { 47 | item := slicesValue.Index(i).Interface() 48 | itemValue := reflect.ValueOf(item) 49 | if !internal.SliceCheck(item) { 50 | panic(fmt.Sprintf("item in %d index is not a slice", i)) 51 | } 52 | if maxLength < itemValue.Len() { 53 | maxLength = itemValue.Len() 54 | } 55 | sizeOfAllSlices += itemValue.Len() 56 | } 57 | 58 | functionInput := reflect.MakeSlice(reflect.TypeOf(slices).Elem(), 0, 0) 59 | for j := 0; j < maxLength; j++ { 60 | for i := 0; i < slicesValue.Len(); i++ { 61 | subSlice := reflect.ValueOf(slicesValue.Index(i).Interface()) 62 | if j >= subSlice.Len() { 63 | continue 64 | } 65 | functionInput = reflect.Append(functionInput, subSlice.Index(j)) 66 | } 67 | funcOutput := reflect.ValueOf(function(functionInput.Interface())) 68 | 69 | output = reflect.Append(output, funcOutput) 70 | functionInput = reflect.Zero(functionInput.Type()) 71 | } 72 | 73 | return output.Interface() 74 | } 75 | -------------------------------------------------------------------------------- /slices/zip_by_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TZipBy struct { 13 | name string 14 | arr interface{} 15 | want interface{} 16 | } 17 | 18 | var tZipByBenchs = []TZipBy{ 19 | { 20 | name: "10", 21 | arr: [][]int{}, 22 | }, 23 | { 24 | name: "100", 25 | arr: [][]int{}, 26 | }, 27 | { 28 | name: "1000", 29 | arr: [][]int{}, 30 | }, 31 | { 32 | name: "10000", 33 | arr: [][]int{}, 34 | }, 35 | { 36 | name: "100000", 37 | arr: [][]int{}, 38 | }, 39 | } 40 | 41 | func init() { 42 | for j := 0; j < len(tZipByBenchs); j++ { 43 | length, _ := strconv.Atoi(tZipByBenchs[j].name) 44 | for i := 0; i < length/10; i++ { 45 | tZipByBenchs[j].arr = append(tZipByBenchs[j].arr.([][]int), []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}) 46 | } 47 | } 48 | } 49 | 50 | func returnZipByTest(inputs interface{}) interface{} { 51 | output := 0 52 | literalInputs := inputs.([]int) 53 | for i := 0; i < len(literalInputs); i++ { 54 | output += literalInputs[i] 55 | } 56 | 57 | return output 58 | } 59 | 60 | func TestZipBy(t *testing.T) { 61 | var tests = []TZipBy{ 62 | { 63 | name: "nil", 64 | arr: nil, 65 | want: nil, 66 | }, 67 | { 68 | name: "error", 69 | arr: nil, 70 | want: nil, 71 | }, 72 | { 73 | name: "empty", 74 | arr: [][]int{}, 75 | want: []int{}, 76 | }, 77 | { 78 | name: "normal", 79 | arr: [][]int{{0, 1}, {2, 3, 4}, {5, 6, 7, 8, 9}}, 80 | want: []int{7, 10, 11, 8, 9}, 81 | }, 82 | { 83 | name: "no change", 84 | arr: [][]int{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}, 85 | want: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 86 | }, 87 | } 88 | 89 | for _, subject := range tests { 90 | t.Run(subject.name, func(t *testing.T) { 91 | defer internal.DeferTestCases(t, subject.want) 92 | got := ZipBy(subject.arr, returnZipByTest) 93 | 94 | if !generals.Same(got, subject.want) { 95 | t.Errorf("got = %v, wanted = %v", got, subject.want) 96 | return 97 | } 98 | }) 99 | } 100 | } 101 | 102 | func BenchmarkZipBy(b *testing.B) { 103 | for j := 0; j < len(tZipByBenchs); j++ { 104 | b.Run(fmt.Sprintf("slice_size_%s", tZipByBenchs[j].name), func(b *testing.B) { 105 | for i := 0; i < b.N; i++ { 106 | ZipBy(tZipByBenchs[j].arr, returnZipByTest) 107 | } 108 | }) 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /slices/zip_map.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // This method is like FromPairs except that it accepts two slices, one of property 10 | // identifiers and one of corresponding values. 11 | // 12 | // Complexity: O(n) 13 | func ZipMap(keys, values interface{}) interface{} { 14 | if !internal.SliceCheck(keys) { 15 | panic("passed 'keys' variable is not slice type") 16 | } 17 | if !internal.SliceCheck(values) { 18 | panic("passed 'values' variable is not slice type") 19 | } 20 | 21 | keysValue := reflect.ValueOf(keys) 22 | valuesValue := reflect.ValueOf(values) 23 | if keysValue.Len() != valuesValue.Len() { 24 | panic("keys and values don't have the same length") 25 | } 26 | outputValues := reflect.MakeMapWithSize(reflect.MapOf(keysValue.Type().Elem(), valuesValue.Type().Elem()), keysValue.Len()) 27 | for i := 0; i < keysValue.Len(); i++ { 28 | outputValues.SetMapIndex(keysValue.Index(i), valuesValue.Index(i)) 29 | } 30 | 31 | return outputValues.Interface() 32 | } 33 | -------------------------------------------------------------------------------- /slices/zip_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TZip struct { 13 | name string 14 | arg interface{} 15 | expected interface{} 16 | } 17 | 18 | var TZipBenchs = []TZip{ 19 | { 20 | name: "10", 21 | arg: [][]interface{}{}, 22 | }, 23 | { 24 | name: "100", 25 | arg: [][]interface{}{}, 26 | }, 27 | { 28 | name: "1000", 29 | arg: [][]interface{}{}, 30 | }, 31 | { 32 | name: "10000", 33 | arg: [][]interface{}{}, 34 | }, 35 | { 36 | name: "100000", 37 | arg: [][]interface{}{}, 38 | }, 39 | } 40 | 41 | func init() { 42 | for i := 0; i < len(TZipBenchs); i++ { 43 | k, _ := strconv.Atoi(TZipBenchs[i].name) 44 | for j := 0; j < k/10; j++ { 45 | TZipBenchs[i].arg = append(TZipBenchs[i].arg.([][]interface{}), []interface{}{"0", 1, true, 'e', false, 5, "name", 7, false, 9}) 46 | } 47 | } 48 | } 49 | 50 | func TestZip(t *testing.T) { 51 | var tests = []TZip{ 52 | { 53 | name: "nil", 54 | arg: nil, 55 | expected: nil, 56 | }, 57 | { 58 | name: "empty", 59 | arg: [][]interface{}{}, 60 | expected: [][]interface{}{}, 61 | }, 62 | { 63 | name: "error", 64 | arg: [][]interface{}{{"a", 1, false, 15}, {"e"}}, 65 | expected: nil, 66 | }, 67 | { 68 | name: "one different slice", 69 | arg: [][]interface{}{{"a", "e"}, {1, 2}, {false, 5}, {15, 16}}, 70 | expected: [][]interface{}{{"a", 1, false, 15}, {"e", 2, 5, 16}}, 71 | }, 72 | { 73 | name: "normal", 74 | arg: [][]interface{}{{"a", "e"}, {1, 2}, {true, false}, {'b', 't'}}, 75 | expected: [][]interface{}{{"a", 1, true, 'b'}, {"e", 2, false, 't'}}, 76 | }, 77 | { 78 | name: "type based", 79 | arg: [][]int{{1, 2}, {3, 4}, {5, 6}, {7, 8}}, 80 | expected: [][]int{{1, 3, 5, 7}, {2, 4, 6, 8}}, 81 | }, 82 | } 83 | 84 | for _, sample := range tests { 85 | t.Run(sample.name, func(t *testing.T) { 86 | defer internal.DeferTestCases(t, sample.expected) 87 | got := Zip(sample.arg) 88 | 89 | if !generals.Same(got, sample.expected) { 90 | t.Errorf("got = %v, wanted = %v", got, sample.expected) 91 | return 92 | } 93 | }) 94 | } 95 | } 96 | 97 | func BenchmarkZip(b *testing.B) { 98 | for _, sample := range TZipBenchs { 99 | b.Run(fmt.Sprintf("input_size_%s", sample.name), func(b *testing.B) { 100 | for i := 0; i < b.N; i++ { 101 | Zip(sample.arg) 102 | } 103 | }) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /strings/camel_case.go: -------------------------------------------------------------------------------- 1 | package strings 2 | 3 | // Converts string to camel case. 4 | // 5 | // Complexity: O(n) 6 | func CamelCase(input string) string { 7 | return internalCamelCase(input, false) 8 | } 9 | -------------------------------------------------------------------------------- /strings/ends_with.go: -------------------------------------------------------------------------------- 1 | package strings 2 | 3 | // Checks if string ends with the given target string. 4 | // 5 | // Complexity: O(1) 6 | func EndsWith(input string, end string) bool { 7 | if len(input) < len(end) { 8 | return false 9 | } 10 | 11 | return input[len(input)-len(end):] == end 12 | } 13 | -------------------------------------------------------------------------------- /strings/ends_with_test.go: -------------------------------------------------------------------------------- 1 | package strings 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TEndsWith struct { 13 | name string 14 | input string 15 | ends string 16 | expected bool 17 | } 18 | 19 | var TEndsWithBenchs = []TEndsWith{ 20 | { 21 | name: "10", 22 | input: "", 23 | ends: "j", 24 | }, 25 | { 26 | name: "100", 27 | input: "", 28 | ends: "j", 29 | }, 30 | { 31 | name: "1000", 32 | input: "", 33 | ends: "j", 34 | }, 35 | { 36 | name: "10000", 37 | input: "", 38 | ends: "j", 39 | }, 40 | { 41 | name: "100000", 42 | input: "", 43 | ends: "j", 44 | }, 45 | } 46 | 47 | func init() { 48 | for i := 0; i < len(TEndsWithBenchs); i++ { 49 | k, _ := strconv.Atoi(TEndsWithBenchs[i].name) 50 | for j := 0; j < k/10; j++ { 51 | TEndsWithBenchs[i].input += "abcdefghij" 52 | } 53 | } 54 | } 55 | 56 | func TestEndsWith(t *testing.T) { 57 | var tests = []TEndsWith{ 58 | { 59 | name: "empty", 60 | input: "", 61 | ends: "", 62 | expected: true, 63 | }, 64 | { 65 | name: "ends empty", 66 | input: "test", 67 | ends: "", 68 | expected: true, 69 | }, 70 | { 71 | name: "input empty", 72 | input: "", 73 | ends: "te", 74 | expected: false, 75 | }, 76 | { 77 | name: "true", 78 | input: "test", 79 | ends: "st", 80 | expected: true, 81 | }, 82 | { 83 | name: "false", 84 | input: "another_test", 85 | ends: "es", 86 | expected: false, 87 | }, 88 | } 89 | 90 | for _, sample := range tests { 91 | t.Run(sample.name, func(t *testing.T) { 92 | defer internal.DeferTestCases(t, sample.expected) 93 | got := EndsWith(sample.input, sample.ends) 94 | 95 | if !generals.Same(got, sample.expected) { 96 | t.Errorf("got = %v, wanted = %v", got, sample.expected) 97 | return 98 | } 99 | }) 100 | } 101 | } 102 | 103 | func BenchmarkEndsWith(b *testing.B) { 104 | for _, sample := range TEndsWithBenchs { 105 | b.Run(fmt.Sprintf("input_size_%s", sample.name), func(b *testing.B) { 106 | for i := 0; i < b.N; i++ { 107 | EndsWith(sample.input, sample.ends) 108 | } 109 | }) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /strings/kebab_case.go: -------------------------------------------------------------------------------- 1 | package strings 2 | 3 | // Converts string to kebab case. 4 | // 5 | // Complexity: O(n) 6 | func KebabCase(input string) string { 7 | return CustomDelimitedCase(input, '-', "", false) 8 | } 9 | -------------------------------------------------------------------------------- /strings/lower_case.go: -------------------------------------------------------------------------------- 1 | package strings 2 | 3 | // Converts string, as space separated words, to lower case. 4 | // 5 | // Complexity: O(n) 6 | func LowerCase(input string) string { 7 | return CustomDelimitedCase(input, ' ', "", false) 8 | } 9 | -------------------------------------------------------------------------------- /strings/lower_first.go: -------------------------------------------------------------------------------- 1 | package strings 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | // Converts the first character of string to lower case. 8 | // 9 | // Complexity: O(n) 10 | func LowerFirst(input string) string { 11 | trimInput := strings.TrimSpace(input) 12 | if len(trimInput) == 0 { 13 | return trimInput 14 | } 15 | 16 | outputBuilder := strings.Builder{} 17 | firstLetter := trimInput[0] 18 | isCap := firstLetter >= 'A' && firstLetter <= 'Z' 19 | 20 | if isCap { 21 | firstLetter += 'a' 22 | firstLetter -= 'A' 23 | } 24 | 25 | outputBuilder.WriteByte(firstLetter) 26 | if len(trimInput) > 1 { 27 | outputBuilder.WriteString(trimInput[1:]) 28 | } 29 | 30 | return outputBuilder.String() 31 | } 32 | -------------------------------------------------------------------------------- /strings/pad.go: -------------------------------------------------------------------------------- 1 | package strings 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | // Pads string on the left and right sides if it's shorter than length. 8 | // Padding characters are truncated if they can't be evenly divided by length. 9 | // 10 | // Complexity: O(n) 11 | // 12 | // n = (length - len(input)) / len(pattern) 13 | func Pad(input string, length int, pattern string) string { 14 | if len(input) >= length || len(pattern) == 0 { 15 | halfRemainingLength := (len(input) - length) / 2 16 | return input[halfRemainingLength+((len(input)-length)%2) : len(input)-halfRemainingLength] 17 | } 18 | 19 | lengthInputDifference := (length - len(input)) 20 | number := (lengthInputDifference / len(pattern)) / 2 21 | outputBuilder := strings.Builder{} 22 | patternBuilder := strings.Builder{} 23 | patternBytes := []byte(pattern) 24 | for i := 0; i < number; i++ { 25 | patternBuilder.Write(patternBytes) 26 | } 27 | 28 | halfRemaining := (lengthInputDifference - patternBuilder.Len()*2) / 2 29 | RestOfHalfRemaining := (lengthInputDifference - patternBuilder.Len()*2) % 2 30 | outputBuilder.WriteString(pattern[len(pattern)-halfRemaining-RestOfHalfRemaining:] + patternBuilder.String()) 31 | outputBuilder.WriteString(input) 32 | outputBuilder.WriteString(patternBuilder.String() + pattern[:halfRemaining]) 33 | 34 | return outputBuilder.String() 35 | } 36 | -------------------------------------------------------------------------------- /strings/pad_end.go: -------------------------------------------------------------------------------- 1 | package strings 2 | 3 | import "strings" 4 | 5 | // Pads string on the left side if it's shorter than length. 6 | // Padding characters are truncated if they exceed length. 7 | // 8 | // Complexity: O(n) 9 | // 10 | // n = (length - len(input)) / len(pattern) 11 | func PadEnd(input string, length int, pattern string) string { 12 | if len(input) >= length || len(pattern) == 0 { 13 | return input[:length] 14 | } 15 | 16 | remaining := (length - len(input)) % len(pattern) 17 | number := (length - len(input)) / len(pattern) 18 | outputBuilder := strings.Builder{} 19 | patternBytes := []byte(pattern) 20 | outputBuilder.WriteString(input) 21 | for i := 0; i < number; i++ { 22 | outputBuilder.Write(patternBytes) 23 | } 24 | if remaining != 0 { 25 | outputBuilder.Write(patternBytes[:remaining]) 26 | } 27 | 28 | return outputBuilder.String() 29 | } 30 | -------------------------------------------------------------------------------- /strings/pad_end_test.go: -------------------------------------------------------------------------------- 1 | package strings 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TPadEnd struct { 13 | name string 14 | input string 15 | pattern string 16 | length int 17 | expected string 18 | } 19 | 20 | var TPadEndBenchs = []TPadEnd{ 21 | { 22 | name: "10", 23 | input: "", 24 | pattern: "|-", 25 | length: 20, 26 | }, 27 | { 28 | name: "100", 29 | input: "", 30 | pattern: "|-", 31 | length: 200, 32 | }, 33 | { 34 | name: "1000", 35 | input: "", 36 | pattern: "|-", 37 | length: 2000, 38 | }, 39 | { 40 | name: "10000", 41 | input: "", 42 | pattern: "|-", 43 | length: 20000, 44 | }, 45 | { 46 | name: "100000", 47 | input: "", 48 | pattern: "|-", 49 | length: 200000, 50 | }, 51 | } 52 | 53 | func init() { 54 | for i := 0; i < len(TPadEndBenchs); i++ { 55 | k, _ := strconv.Atoi(TPadEndBenchs[i].name) 56 | for j := 0; j < k/10; j++ { 57 | TPadEndBenchs[i].input += "kmsa daewq" 58 | } 59 | } 60 | } 61 | 62 | func TestPadEnd(t *testing.T) { 63 | var tests = []TPadEnd{ 64 | { 65 | name: "0", 66 | input: "This is a freaking test just for the fun of it!??", 67 | length: 53, 68 | pattern: "_-", 69 | expected: "This is a freaking test just for the fun of it!??_-_-", 70 | }, 71 | { 72 | name: "1", 73 | input: "This is a freaking test just for the fun of it!??", 74 | length: 54, 75 | pattern: "_-", 76 | expected: "This is a freaking test just for the fun of it!??_-_-_", 77 | }, 78 | { 79 | name: "2", 80 | input: "This is a freaking test just for the fun of it!??", 81 | length: 43, 82 | pattern: "_-", 83 | expected: "This is a freaking test just for the fun of", 84 | }, 85 | } 86 | 87 | for _, sample := range tests { 88 | t.Run(sample.name, func(t *testing.T) { 89 | defer internal.DeferTestCases(t, sample.expected) 90 | got := PadEnd(sample.input, sample.length, sample.pattern) 91 | 92 | if !generals.Same(got, sample.expected) { 93 | t.Errorf("got = %v, wanted = %v", got, sample.expected) 94 | return 95 | } 96 | }) 97 | } 98 | } 99 | 100 | func BenchmarkPadEnd(b *testing.B) { 101 | for _, sample := range TPadEndBenchs { 102 | b.Run(fmt.Sprintf("input_size_%s", sample.name), func(b *testing.B) { 103 | for i := 0; i < b.N; i++ { 104 | PadEnd(sample.input, sample.length, sample.pattern) 105 | } 106 | }) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /strings/pad_start.go: -------------------------------------------------------------------------------- 1 | package strings 2 | 3 | import "strings" 4 | 5 | // Pads string on the left side if it's shorter than length. 6 | // Padding characters are truncated if they exceed length. 7 | // 8 | // Complexity: O(n) 9 | // 10 | // n = (length - len(input)) / len(pattern) 11 | func PadStart(input string, length int, pattern string) string { 12 | if len(input) >= length || len(pattern) == 0 { 13 | return input[len(input)-length:] 14 | } 15 | 16 | remaining := (length - len(input)) % len(pattern) 17 | number := (length - len(input)) / len(pattern) 18 | outputBuilder := strings.Builder{} 19 | patternBytes := []byte(pattern) 20 | outputBuilder.Write(patternBytes[len(pattern)-remaining:]) 21 | for i := 0; i < number; i++ { 22 | outputBuilder.Write(patternBytes) 23 | } 24 | 25 | outputBuilder.WriteString(input) 26 | return outputBuilder.String() 27 | } 28 | -------------------------------------------------------------------------------- /strings/pascal_case.go: -------------------------------------------------------------------------------- 1 | package strings 2 | 3 | // Converts string to pascal case. 4 | // 5 | // Complexity: O(n) 6 | func PascalCase(input string) string { 7 | return internalCamelCase(input, true) 8 | } 9 | -------------------------------------------------------------------------------- /strings/repeat.go: -------------------------------------------------------------------------------- 1 | package strings 2 | 3 | import "strings" 4 | 5 | // Repeats the given string count times. 6 | // 7 | // Complexity: O(n) 8 | // 9 | // n = count 10 | func Repeat(input string, count int) string { 11 | if len(input) == 0 || count < 1 { 12 | return "" 13 | } 14 | 15 | outputBuilder := strings.Builder{} 16 | outputBuilder.Grow(count * len(input)) 17 | for i := 0; i < count; i++ { 18 | outputBuilder.WriteString(input) 19 | } 20 | 21 | return outputBuilder.String() 22 | } 23 | -------------------------------------------------------------------------------- /strings/repeat_test.go: -------------------------------------------------------------------------------- 1 | package strings 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TRepeat struct { 13 | name string 14 | input string 15 | count int 16 | expected string 17 | } 18 | 19 | var TRepeatBenchs = []TRepeat{ 20 | { 21 | name: "10", 22 | input: "a", 23 | count: 10, 24 | }, 25 | { 26 | name: "100", 27 | input: "a", 28 | count: 100, 29 | }, 30 | { 31 | name: "1000", 32 | input: "a", 33 | count: 1000, 34 | }, 35 | { 36 | name: "10000", 37 | input: "a", 38 | count: 10000, 39 | }, 40 | // { 41 | // name: "100000", 42 | // input: "a", 43 | // count: 100000, 44 | // }, 45 | } 46 | 47 | func init() { 48 | for i := 0; i < len(TRepeatBenchs); i++ { 49 | k, _ := strconv.Atoi(TRepeatBenchs[i].name) 50 | for j := 0; j < k/10; j++ { 51 | TRepeatBenchs[i].input += "abcdefghij" 52 | } 53 | } 54 | } 55 | 56 | func TestRepeat(t *testing.T) { 57 | var tests = []TRepeat{ 58 | { 59 | name: "empty", 60 | input: "", 61 | count: 0, 62 | expected: "", 63 | }, 64 | { 65 | name: "zero count", 66 | input: "abc", 67 | count: 0, 68 | expected: "", 69 | }, 70 | { 71 | name: "input empty", 72 | input: "", 73 | count: 2, 74 | expected: "", 75 | }, 76 | { 77 | name: "normal", 78 | input: "abc", 79 | count: 3, 80 | expected: "abcabcabc", 81 | }, 82 | { 83 | name: "negative count", 84 | input: "abc", 85 | count: -1, 86 | expected: "", 87 | }, 88 | } 89 | 90 | for _, sample := range tests { 91 | t.Run(sample.name, func(t *testing.T) { 92 | defer internal.DeferTestCases(t, sample.expected) 93 | got := Repeat(sample.input, sample.count) 94 | 95 | if !generals.Same(got, sample.expected) { 96 | t.Errorf("got = %v, wanted = %v", got, sample.expected) 97 | return 98 | } 99 | }) 100 | } 101 | } 102 | 103 | func BenchmarkRepeat(b *testing.B) { 104 | for _, sample := range TRepeatBenchs { 105 | b.Run(fmt.Sprintf("input_size_%s", sample.name), func(b *testing.B) { 106 | for i := 0; i < b.N; i++ { 107 | Repeat(sample.input, sample.count) 108 | } 109 | }) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /strings/snake_case.go: -------------------------------------------------------------------------------- 1 | package strings 2 | 3 | // Converts string to snake case. 4 | // 5 | // Complexity: O(n) 6 | func SnakeCase(input string) string { 7 | return CustomDelimitedCase(input, '_', "", false) 8 | } 9 | -------------------------------------------------------------------------------- /strings/start_case.go: -------------------------------------------------------------------------------- 1 | package strings 2 | 3 | import "strings" 4 | 5 | // Converts string to Start Case. 6 | // 7 | // Complexity: O(n) 8 | func StartCase(input string) string { 9 | return startCase(CustomDelimitedCase(input, ' ', "", false)) 10 | } 11 | 12 | func startCase(input string) string { 13 | upper := true 14 | outputBuilder := strings.Builder{} 15 | outputBuilder.Grow(len(input)) 16 | for _, letter := range []byte(input) { 17 | isLow := letter >= 'a' && letter <= 'z' 18 | isSpace := letter == ' ' 19 | 20 | if isLow && upper { 21 | letter += 'A' 22 | letter -= 'a' 23 | } 24 | upper = false 25 | 26 | if isSpace { 27 | upper = true 28 | } 29 | 30 | outputBuilder.WriteByte(letter) 31 | } 32 | 33 | return outputBuilder.String() 34 | } 35 | -------------------------------------------------------------------------------- /strings/starts_with.go: -------------------------------------------------------------------------------- 1 | package strings 2 | 3 | // Checks if string starts with the given target string. 4 | // 5 | // Complexity: O(1) 6 | func StartsWith(input string, start string) bool { 7 | if len(input) < len(start) { 8 | return false 9 | } 10 | 11 | return input[:len(start)] == start 12 | } 13 | -------------------------------------------------------------------------------- /strings/starts_with_test.go: -------------------------------------------------------------------------------- 1 | package strings 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TStartsWith struct { 13 | name string 14 | input string 15 | starts string 16 | expected bool 17 | } 18 | 19 | var TStartsWithBenchs = []TStartsWith{ 20 | { 21 | name: "10", 22 | input: "", 23 | starts: "j", 24 | }, 25 | { 26 | name: "100", 27 | input: "", 28 | starts: "j", 29 | }, 30 | { 31 | name: "1000", 32 | input: "", 33 | starts: "j", 34 | }, 35 | { 36 | name: "10000", 37 | input: "", 38 | starts: "j", 39 | }, 40 | { 41 | name: "100000", 42 | input: "", 43 | starts: "j", 44 | }, 45 | } 46 | 47 | func init() { 48 | for i := 0; i < len(TStartsWithBenchs); i++ { 49 | k, _ := strconv.Atoi(TStartsWithBenchs[i].name) 50 | for j := 0; j < k/10; j++ { 51 | TStartsWithBenchs[i].input += "abcdefghij" 52 | } 53 | } 54 | } 55 | 56 | func TestStartsWith(t *testing.T) { 57 | var tests = []TStartsWith{ 58 | { 59 | name: "empty", 60 | input: "", 61 | starts: "", 62 | expected: true, 63 | }, 64 | { 65 | name: "starts empty", 66 | input: "test", 67 | starts: "", 68 | expected: true, 69 | }, 70 | { 71 | name: "input empty", 72 | input: "", 73 | starts: "te", 74 | expected: false, 75 | }, 76 | { 77 | name: "true", 78 | input: "test", 79 | starts: "te", 80 | expected: true, 81 | }, 82 | { 83 | name: "false", 84 | input: "another_test", 85 | starts: "no", 86 | expected: false, 87 | }, 88 | } 89 | 90 | for _, sample := range tests { 91 | t.Run(sample.name, func(t *testing.T) { 92 | defer internal.DeferTestCases(t, sample.expected) 93 | got := StartsWith(sample.input, sample.starts) 94 | 95 | if !generals.Same(got, sample.expected) { 96 | t.Errorf("got = %v, wanted = %v", got, sample.expected) 97 | return 98 | } 99 | }) 100 | } 101 | } 102 | 103 | func BenchmarkStartsWith(b *testing.B) { 104 | for _, sample := range TStartsWithBenchs { 105 | b.Run(fmt.Sprintf("input_size_%s", sample.name), func(b *testing.B) { 106 | for i := 0; i < b.N; i++ { 107 | StartsWith(sample.input, sample.starts) 108 | } 109 | }) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /strings/truncate.go: -------------------------------------------------------------------------------- 1 | package strings 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | // Truncates string if it's longer than the given maximum string length. 10 | // The last characters of the truncated string are replaced with the 11 | // omission string. 12 | // 13 | // Complexity: O(n) 14 | func Truncate(input string, length int, separators []rune, omission string) string { 15 | tempInput := strings.TrimSpace(input) 16 | if len(tempInput) <= length { 17 | return tempInput 18 | } 19 | 20 | tempInput = tempInput[:length] 21 | i := length - 1 22 | for ; i > -1; i-- { 23 | if internal.CustomIsSeparator(rune(tempInput[i]), separators) { 24 | tempInput = tempInput[:i] 25 | break 26 | } 27 | } 28 | 29 | return strings.TrimSpace(tempInput) + omission 30 | } 31 | -------------------------------------------------------------------------------- /strings/truncate_test.go: -------------------------------------------------------------------------------- 1 | package strings 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/golodash/godash/generals" 9 | "github.com/golodash/godash/internal" 10 | ) 11 | 12 | type TTruncate struct { 13 | name string 14 | input string 15 | length int 16 | expected string 17 | } 18 | 19 | var TTruncateBenchs = []TTruncate{ 20 | { 21 | name: "10", 22 | input: "", 23 | length: 9, 24 | }, 25 | { 26 | name: "100", 27 | input: "", 28 | length: 99, 29 | }, 30 | { 31 | name: "1000", 32 | input: "", 33 | length: 999, 34 | }, 35 | { 36 | name: "10000", 37 | input: "", 38 | length: 9999, 39 | }, 40 | { 41 | name: "100000", 42 | input: "", 43 | length: 99999, 44 | }, 45 | } 46 | 47 | func init() { 48 | for i := 0; i < len(TTruncateBenchs); i++ { 49 | k, _ := strconv.Atoi(TTruncateBenchs[i].name) 50 | for j := 0; j < k/10; j++ { 51 | TTruncateBenchs[i].input += "kmsa daewq" 52 | } 53 | } 54 | } 55 | 56 | var separators = []rune{' ', '.', '?', '!'} 57 | 58 | func TestTruncate(t *testing.T) { 59 | var tests = []TTruncate{ 60 | { 61 | name: "0", 62 | input: " This is a freaking test just for the fun of it!??", 63 | length: 20, 64 | expected: "This is a freaking...", 65 | }, 66 | { 67 | name: "1", 68 | input: " This is a freaking test just for the fun of it!??", 69 | length: 100, 70 | expected: "This is a freaking test just for the fun of it!??", 71 | }, 72 | { 73 | name: "2", 74 | input: " This is a freaking test just for the fun of it!??", 75 | length: 6, 76 | expected: "This...", 77 | }, 78 | } 79 | 80 | for _, sample := range tests { 81 | t.Run(sample.name, func(t *testing.T) { 82 | defer internal.DeferTestCases(t, sample.expected) 83 | got := Truncate(sample.input, sample.length, separators, "...") 84 | 85 | if !generals.Same(got, sample.expected) { 86 | t.Errorf("got = %v, wanted = %v", got, sample.expected) 87 | return 88 | } 89 | }) 90 | } 91 | } 92 | 93 | func BenchmarkTruncate(b *testing.B) { 94 | for _, sample := range TTruncateBenchs { 95 | b.Run(fmt.Sprintf("input_size_%s", sample.name), func(b *testing.B) { 96 | for i := 0; i < b.N; i++ { 97 | Truncate(sample.input, sample.length, separators, "...") 98 | } 99 | }) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /strings/upper_first.go: -------------------------------------------------------------------------------- 1 | package strings 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | // Converts the first character of string to upper case. 8 | // 9 | // Complexity: O(n) 10 | func UpperFirst(input string) string { 11 | trimInput := strings.TrimSpace(input) 12 | if len(trimInput) == 0 { 13 | return trimInput 14 | } 15 | 16 | outputBuilder := strings.Builder{} 17 | firstLetter := trimInput[0] 18 | isLow := firstLetter >= 'a' && firstLetter <= 'z' 19 | 20 | if isLow { 21 | firstLetter += 'A' 22 | firstLetter -= 'a' 23 | } 24 | 25 | outputBuilder.WriteByte(firstLetter) 26 | if len(trimInput) > 1 { 27 | outputBuilder.WriteString(trimInput[1:]) 28 | } 29 | 30 | return outputBuilder.String() 31 | } 32 | -------------------------------------------------------------------------------- /strings/words.go: -------------------------------------------------------------------------------- 1 | package strings 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/golodash/godash/internal" 7 | ) 8 | 9 | var wordsSeparators = []rune{',', '.', '/', '\\', '\'', '"', ':', ';', ']', '[', '=', '+', '-', ')', '(', '*', '&', '^', '%', '$', '#', '@', '!', '~', '`', '|', '?', ' ', '\t', '\n', '_', '<', '>', '}', '{'} 10 | 11 | // Splits string into a slice of its words. 12 | // 13 | // Complexity: O(n) 14 | func Words(input string) []string { 15 | outputSlice := []string{} 16 | sawOneSeparatorLastTime := false 17 | wordBuilder := strings.Builder{} 18 | for _, letter := range []byte(input) { 19 | if internal.CustomIsSeparator(rune(letter), wordsSeparators) { 20 | if !sawOneSeparatorLastTime && wordBuilder.Len() != 0 { 21 | outputSlice = append(outputSlice, wordBuilder.String()) 22 | wordBuilder.Reset() 23 | } 24 | sawOneSeparatorLastTime = true 25 | } else { 26 | wordBuilder.WriteByte(letter) 27 | sawOneSeparatorLastTime = false 28 | } 29 | } 30 | 31 | if wordBuilder.Len() != 0 { 32 | outputSlice = append(outputSlice, wordBuilder.String()) 33 | } 34 | 35 | return outputSlice 36 | } 37 | --------------------------------------------------------------------------------