Skip to content

Commit

Permalink
Add IndexOf/IndexOfT function (#79)
Browse files Browse the repository at this point in the history
This includes the IndexOf function and IndexOfT, as well as a small
example of how to use the function to update items in the array.
  • Loading branch information
elliotcourant authored and ahmetb committed Jun 7, 2019
1 parent 9f6960b commit 9bb035d
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 0 deletions.
41 changes: 41 additions & 0 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,47 @@ func ExampleQuery_GroupJoin() {
// [{a [apple apricot]} {b [banana]} {c [cherry clementine]}]
}

// The following code example demonstrates how to use IndexOf
// to retrieve the position of an item in the array and then
// update that item.
func ExampleQuery_IndexOf() {
type Item struct {
ID uint64
Name string
}
items := []Item{
{
ID: 1,
Name: "Joe",
},
{
ID: 2,
Name: "Bob",
},
{
ID: 3,
Name: "Rickster",
},
{
ID: 4,
Name: "Jim",
},
}

index := From(items).IndexOf(func(i interface{}) bool {
item, ok := i.(Item)
return ok && item.Name == "Rickster"
})

if index >= 0 {
// We found the item in the array. Change the name using the index.
items[index].Name = "Joshua"
fmt.Println("Item found at:", index, "new name:", items[index].Name)
}
// Output:
// Item found at: 2 new name: Joshua
}

// The following code example demonstrates how to use Join
// to perform an inner join of two slices based on a common key.
func ExampleQuery_Join() {
Expand Down
40 changes: 40 additions & 0 deletions index.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package linq

// IndexOf searches for an element that matches the conditions defined by a specified predicate
// and returns the zero-based index of the first occurrence within the collection. This method
// returns -1 if an item that matches the conditions is not found.
func (q Query) IndexOf(predicate func(interface{}) bool) int {
index := 0
next := q.Iterate()

for item, ok := next(); ok; item, ok = next() {
if predicate(item) {
return index
}
index++
}

return -1
}

// IndexOfT is the typed version of IndexOf.
//
// - predicateFn is of type "func(int,TSource)bool"
//
// NOTE: IndexOf has better performance than IndexOfT.
func (q Query) IndexOfT(predicateFn interface{}) int {

predicateGenericFunc, err := newGenericFunc(
"IndexOfT", "predicateFn", predicateFn,
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))),
)
if err != nil {
panic(err)
}

predicateFunc := func(item interface{}) bool {
return predicateGenericFunc.Call(item).(bool)
}

return q.IndexOf(predicateFunc)
}
53 changes: 53 additions & 0 deletions index_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package linq

import (
"testing"
)

func TestIndexOf(t *testing.T) {
tests := []struct {
input interface{}
predicate func(interface{}) bool
expected int
}{
{
input: [9]int{1, 2, 3, 4, 5, 6, 7, 8, 9},
predicate: func(i interface{}) bool {
return i.(int) == 3
},
expected: 2,
},
{
input: "sstr",
predicate: func(i interface{}) bool {
return i.(rune) == 'r'
},
expected: 3,
},
{
input: "gadsgsadgsda",
predicate: func(i interface{}) bool {
return i.(rune) == 'z'
},
expected: -1,
},
}

for _, test := range tests {
index := From(test.input).IndexOf(test.predicate)
if index != test.expected {
t.Errorf("From(%v).IndexOf() expected %v received %v", test.input, test.expected, index)
}

index = From(test.input).IndexOfT(test.predicate)
if index != test.expected {
t.Errorf("From(%v).IndexOfT() expected %v received %v", test.input, test.expected, index)
}
}
}

func TestIndexOfT_PanicWhenPredicateFnIsInvalid(t *testing.T) {
mustPanicWithError(t, "IndexOfT: parameter [predicateFn] has a invalid function signature. Expected: 'func(T)bool', actual: 'func(int)int'", func() {
From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).IndexOfT(func(item int) int { return item + 2 })
})
}

0 comments on commit 9bb035d

Please sign in to comment.