# artemkorsakov's solution

## to Strain in the Go Track

Published at Feb 25 2019 · 0 comments
Instructions
Test suite
Solution

Implement the `keep` and `discard` operation on collections. Given a collection and a predicate on the collection's elements, `keep` returns a new collection containing those elements where the predicate is true, while `discard` returns a new collection containing those elements where the predicate is false.

For example, given the collection of numbers:

• 1, 2, 3, 4, 5

And the predicate:

• is the number even?

Then your keep operation should produce:

• 2, 4

• 1, 3, 5

Note that the union of keep and discard is all the elements.

The functions may be called `keep` and `discard`, or they may need different names in order to not clash with existing functions or concepts in your language.

## Restrictions

Keep your hands off that filter/reject/whatchamacallit functionality provided by your standard library! Solve this one yourself using other basic tools instead.

## Running the tests

To run the tests run the command `go test` from within the exercise directory.

If the test suite contains benchmarks, you can run these with the `--bench` and `--benchmem` flags:

``````go test -v --bench . --benchmem
``````

Keep in mind that each reviewer will run benchmarks on a different machine, with different specs, so the results from these benchmark tests may vary.

## Further information

For more detailed information about the Go track, including how to get help if you're having trouble, please visit the exercism.io Go language page.

## Source

Conversation with James Edward Gray II https://twitter.com/jeg2

## Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

### strain_test.go

``````// Collections, hm?  For this exercise in Go you'll work with slices as
// collections.  Define the following in your solution:
//
//    type Ints []int
//    type Lists [][]int
//    type Strings []string
//
// Then complete the exercise by implementing these methods:
//
//    (Ints) Keep(func(int) bool) Ints
//    (Lists) Keep(func([]int) bool) Lists
//    (Strings) Keep(func(string) bool) Strings

package strain

import (
"reflect"
"testing"
)

func lt10(x int) bool { return x < 10 }
func gt10(x int) bool { return x > 10 }
func odd(x int) bool  { return x&1 == 1 }
func even(x int) bool { return x&1 == 0 }

var keepTests = []struct {
pred func(int) bool
list Ints
want Ints
}{
{lt10,
nil,
nil},
{lt10,
Ints{1, 2, 3},
Ints{1, 2, 3}},
{odd,
Ints{1, 2, 3},
Ints{1, 3}},
{even,
Ints{1, 2, 3, 4, 5},
Ints{2, 4}},
}

pred func(int) bool
list Ints
want Ints
}{
{lt10,
nil,
nil},
{gt10,
Ints{1, 2, 3},
Ints{1, 2, 3}},
{odd,
Ints{1, 2, 3},
Ints{2}},
{even,
Ints{1, 2, 3, 4, 5},
Ints{1, 3, 5}},
}

func TestKeepInts(t *testing.T) {
for _, test := range keepTests {
// setup here copies test.list, preserving the nil value if it is nil
// and making a fresh copy of the underlying array otherwise.
cp := test.list
if cp != nil {
cp = append(Ints{}, cp...)
}
switch res := cp.Keep(test.pred); {
case !reflect.DeepEqual(cp, test.list):
t.Fatalf("%#v.Keep() should not modify its receiver.  "+
"Found %#v, receiver should stay %#v",
test.list, cp, test.list)
case !reflect.DeepEqual(res, test.want):
t.Fatalf("%#v.Keep()\ngot: %#v\nwant: %#v",
test.list, res, test.want)
}
}
}

for _, test := range discardTests {
cp := test.list
if cp != nil {
cp = append(Ints{}, cp...) // dup underlying array
}
case !reflect.DeepEqual(cp, test.list):
"Found %#v, receiver should stay %#v",
test.list, cp, test.list)
case !reflect.DeepEqual(res, test.want):
test.list, res, test.want)
}
}
}

func TestKeepStrings(t *testing.T) {
zword := func(s string) bool { return len(s) > 0 && s[0] == 'z' }
list := Strings{"apple", "zebra", "banana", "zombies", "cherimoya", "zealot"}
want := Strings{"zebra", "zombies", "zealot"}

cp := append(Strings{}, list...) // make copy, as with TestInts
switch res := cp.Keep(zword); {
case !reflect.DeepEqual(cp, list):
t.Fatalf("%#v.Keep() should not modify its receiver.  "+
"Found %#v, receiver should stay %#v",
list, cp, list)
case !reflect.DeepEqual(res, want):
t.Fatalf("%#v.Keep()\ngot: %#v\nwant: %#v", list, res, want)
}
}

func TestKeepLists(t *testing.T) {
has5 := func(l []int) bool {
for _, e := range l {
if e == 5 {
return true
}
}
return false
}
list := Lists{
{1, 2, 3},
{5, 5, 5},
{5, 1, 2},
{2, 1, 2},
{1, 5, 2},
{2, 2, 1},
{1, 2, 5},
}
want := Lists{
{5, 5, 5},
{5, 1, 2},
{1, 5, 2},
{1, 2, 5},
}
cp := append(Lists{}, list...)
switch res := cp.Keep(has5); {
case !reflect.DeepEqual(cp, list):
t.Fatalf("%#v.Keep() should not modify its receiver.  "+
"Found %#v, receiver should stay %#v",
list, cp, list)
case !reflect.DeepEqual(res, want):
t.Fatalf("%#v.Keep()\ngot: %#v\nwant: %#v", list, res, want)
}
}

func BenchmarkKeepInts(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, test := range keepTests {
test.list.Keep(test.pred)
}
}
}

for i := 0; i < b.N; i++ {
for _, test := range discardTests {
}
}
}``````
``````package strain

// Ints is a list of integer.
type Ints []int

// Strings is a list of string.
type Strings []string

// Lists is a list of lists of integer.
type Lists [][]int

// Keep returns a sub-list of elements whose function returns true.
func (ints Ints) Keep(pred func(int) bool) Ints {
if ints == nil {
return nil
}
res := make([]int, 0)
for _, num := range ints {
if pred(num) {
res = append(res, num)
}
}
return res
}

// Discard returns a sub-list of elements whose function returns false.
func (ints Ints) Discard(pred func(int) bool) Ints {
if ints == nil {
return nil
}
res := make([]int, 0)
for _, num := range ints {
if !pred(num) {
res = append(res, num)
}
}
return res
}

// Keep returns a sub-list of elements whose function returns true.
func (strings Strings) Keep(pred func(string) bool) Strings {
res := make([]string, 0)
for _, str := range strings {
if pred(str) {
res = append(res, str)
}
}
return res
}

// Keep returns a sub-list of elements whose function returns true.
func (lists Lists) Keep(pred func([]int) bool) Lists {
res := make([][]int, 0)
for _, list := range lists {
if pred(list) {
res = append(res, list)
}
}
return res
}``````