From 55c110a6747c27db162e90a4ceb1749ab8975529 Mon Sep 17 00:00:00 2001 From: Cleiton Marques Date: Fri, 5 Jun 2020 09:26:22 -0700 Subject: [PATCH] Adds DefaultIfEmpty function (#95) Co-authored-by: Cleiton Marques Souza --- defaultifempty.go | 33 ++++++++++++++++++++ defaultifempty_test.go | 25 +++++++++++++++ example_test.go | 70 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 defaultifempty.go create mode 100644 defaultifempty_test.go diff --git a/defaultifempty.go b/defaultifempty.go new file mode 100644 index 0000000..0bf7246 --- /dev/null +++ b/defaultifempty.go @@ -0,0 +1,33 @@ +package linq + +// DefaultIfEmpty returns the elements of the specified sequence +// if the sequence is empty. +func (q Query) DefaultIfEmpty(defaultValue interface{}) Query { + return Query{ + Iterate: func() Iterator { + next := q.Iterate() + state := 1 + + return func() (item interface{}, ok bool) { + switch state { + case 1: + item, ok = next() + if ok { + state = 2 + } else { + item = defaultValue + ok = true + state = -1 + } + return + case 2: + for item, ok = next(); ok; item, ok = next() { + return + } + return + } + return + } + }, + } +} diff --git a/defaultifempty_test.go b/defaultifempty_test.go new file mode 100644 index 0000000..55025a0 --- /dev/null +++ b/defaultifempty_test.go @@ -0,0 +1,25 @@ +package linq + +import ( + "testing" +) + +func TestDefaultIfEmpty(t *testing.T) { + defaultValue := 0 + tests := []struct { + input []interface{} + want []interface{} + }{ + {[]interface{}{}, []interface{}{defaultValue}}, + {[]interface{}{1, 2, 3, 4, 5}, []interface{}{1, 2, 3, 4, 5}}, + } + + for _, test := range tests { + q := From(test.input).DefaultIfEmpty(defaultValue) + + if !validateQuery(q, test.want) { + t.Errorf("From(%v).DefaultIfEmpty(%v)=%v expected %v", test.input, defaultValue, toSlice(q), test.want) + } + } + +} diff --git a/example_test.go b/example_test.go index a46af5b..4471957 100644 --- a/example_test.go +++ b/example_test.go @@ -375,6 +375,76 @@ func ExampleQuery_CountWith() { // 6 } +// The following example demonstrates how to use the DefaultIfEmpty +// method on the results of a group join to perform a left outer join. +// +// The first step in producing a left outer join of two collections is to perform +// an inner join by using a group join. In this example, the list of Person objects +// is inner-joined to the list of Pet objects based on a Person object that matches Pet.Owner. +// +// The second step is to include each element of the first (left) collection in the +// result set even if that element has no matches in the right collection. +// This is accomplished by calling DefaultIfEmpty on each sequence of matching +// elements from the group join. +// In this example, DefaultIfEmpty is called on each sequence of matching Pet elements. +// The method returns a collection that contains a single, default value if the sequence +// of matching Pet elements is empty for any Person element, thereby ensuring that each +// Person element is represented in the result collection. +func ExampleQuery_DefaultIfEmpty() { + type Person struct { + FirstName string + LastName string + } + + type Pet struct { + Name string + Owner Person + } + + magnus := Person{FirstName: "Magnus", LastName: "Hedlund"} + terry := Person{FirstName: "Terry", LastName: "Adams"} + charlotte := Person{FirstName: "Charlotte", LastName: "Weiss"} + arlene := Person{FirstName: "Arlene", LastName: "Huff"} + + barley := Pet{Name: "Barley", Owner: terry} + boots := Pet{Name: "Boots", Owner: terry} + whiskers := Pet{Name: "Whiskers", Owner: charlotte} + bluemoon := Pet{Name: "Blue Moon", Owner: terry} + daisy := Pet{Name: "Daisy", Owner: magnus} + + // Create two lists. + people := []Person{magnus, terry, charlotte, arlene} + pets := []Pet{barley, boots, whiskers, bluemoon, daisy} + + results := []string{} + From(people). + GroupJoinT( + From(pets), + func(person Person) Person { return person }, + func(pet Pet) Person { return pet.Owner }, + func(person Person, pets []Pet) Group { return Group{Key: person, Group: From(pets).Results()} }, + ). + SelectManyByT( + func(g Group) Query { return From(g.Group).DefaultIfEmpty(Pet{}) }, + func(pet Pet, group Group) string { + return fmt.Sprintf("%s: %s", group.Key.(Person).FirstName, pet.Name) + }, + ). + ToSlice(&results) + + for _, s := range results { + fmt.Println(s) + } + // Output: + // Magnus: Daisy + // Terry: Barley + // Terry: Boots + // Terry: Blue Moon + // Charlotte: Whiskers + // Arlene: + +} + //The following code example demonstrates how to use Distinct //to return distinct elements from a slice of integers. func ExampleQuery_Distinct() {