diff --git a/example_test.go b/example_test.go index 401e714..60a891d 100644 --- a/example_test.go +++ b/example_test.go @@ -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() { diff --git a/index.go b/index.go new file mode 100644 index 0000000..cd348c0 --- /dev/null +++ b/index.go @@ -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) +} diff --git a/index_test.go b/index_test.go new file mode 100644 index 0000000..38635e4 --- /dev/null +++ b/index_test.go @@ -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 }) + }) +}