ðŸŽ‰ Exercism Research is now launched. Help Exercism, help science and have some fun at research.exercism.io ðŸŽ‰

jaeyson's solution

to Strain in the Elixir Track

Published at Feb 07 2021 · 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.

`apply` will let you pass arguments to a function, as will `fun.(args)`

Running tests

Execute the tests with:

``````\$ mix test
``````

Pending tests

In the test suites, all but the first test have been skipped.

Once you get a test passing, you can unskip the next one by commenting out the relevant `@tag :pending` with a `#` symbol.

For example:

``````# @tag :pending
test "shouting" do
assert Bob.hey("WATCH OUT!") == "Whoa, chill out!"
end
``````

Or, you can enable all the tests by commenting out the `ExUnit.configure` line in the test suite.

``````# ExUnit.configure exclude: :pending, trace: true
``````

If you're stuck on something, it may help to look at some of the available resources out there where answers might be found.

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.exs

``````defmodule StrainTest do
use ExUnit.Case

defp is_odd?(n), do: rem(n, 2) == 1
defp is_even?(n), do: rem(n, 2) == 0
defp noop(_), do: true

# @tag :pending
test "empty keep" do
assert Strain.keep([], &noop/1) == []
end

@tag :pending
test "keep everything" do
assert Strain.keep([1, 2, 3], fn e -> e < 10 end) == [1, 2, 3]
end

@tag :pending
test "keep first and last" do
assert Strain.keep([1, 2, 3], &is_odd?/1) == [1, 3]
end

@tag :pending
test "keep neither first nor last" do
assert Strain.keep([1, 2, 3, 4, 5], &is_even?/1) == [2, 4]
end

@tag :pending
test "keep strings" do
words = ~w(apple zebra banana zombies cherimoya zelot)
assert Strain.keep(words, &String.starts_with?(&1, "z")) == ~w(zebra zombies zelot)
end

@tag :pending
test "keep arrays" do
rows = [
[1, 2, 3],
[5, 5, 5],
[5, 1, 2],
[2, 1, 2],
[1, 5, 2],
[2, 2, 1],
[1, 2, 5]
]

assert Strain.keep(rows, fn row -> 5 in row end) == [
[5, 5, 5],
[5, 1, 2],
[1, 5, 2],
[1, 2, 5]
]
end

@tag :pending
end

@tag :pending
assert Strain.discard([1, 2, 3], fn e -> e > 10 end) == [1, 2, 3]
end

@tag :pending
test "discard first and last" do
assert Strain.discard([1, 2, 3], &is_odd?/1) == [2]
end

@tag :pending
test "discard neither first nor last" do
assert Strain.discard([1, 2, 3, 4, 5], &is_even?/1) == [1, 3, 5]
end

@tag :pending
words = ~w(apple zebra banana zombies cherimoya zelot)
assert Strain.discard(words, &String.starts_with?(&1, "z")) == ~w(apple banana cherimoya)
end

@tag :pending
rows = [
[1, 2, 3],
[5, 5, 5],
[5, 1, 2],
[2, 1, 2],
[1, 5, 2],
[2, 2, 1],
[1, 2, 5]
]

assert Strain.discard(rows, fn row -> 5 in row end) == [[1, 2, 3], [2, 1, 2], [2, 2, 1]]
end
end``````

test_helper.exs

``````ExUnit.start()
ExUnit.configure(exclude: :pending, trace: true)``````

lib/strain.ex

``````defmodule Strain do
@doc """
Given a `list` of items and a function `fun`, return the list of items where
`fun` returns true.

Do not use `Enum.filter`.
"""
@spec keep(list :: list(any), fun :: (any -> boolean)) :: list(any)
def keep([], _func), do: []
def keep([head | tail], fun) do
true ->
false ->
keep(tail, fun)
end
end

@doc """
Given a `list` of items and a function `fun`, return the list of items where
`fun` returns false.

Do not use `Enum.reject`.
"""
@spec discard(list :: list(any), fun :: (any -> boolean)) :: list(any)
true ->
false ->
end
end
end``````

test/strain_test.exs

``````defmodule StrainTest do
use ExUnit.Case

defp is_odd?(n), do: rem(n, 2) == 1
defp is_even?(n), do: rem(n, 2) == 0
defp noop(_), do: true

# @tag :pending
test "empty keep" do
assert Strain.keep([], &noop/1) == []
end

# @tag :pending
test "keep everything" do
assert Strain.keep([1, 2, 3], fn e -> e < 10 end) == [1, 2, 3]
end

# @tag :pending
test "keep first and last" do
assert Strain.keep([1, 2, 3], &is_odd?/1) == [1, 3]
end

# @tag :pending
test "keep neither first nor last" do
assert Strain.keep([1, 2, 3, 4, 5], &is_even?/1) == [2, 4]
end

# @tag :pending
test "keep strings" do
words = ~w(apple zebra banana zombies cherimoya zelot)
assert Strain.keep(words, &String.starts_with?(&1, "z")) == ~w(zebra zombies zelot)
end

# @tag :pending
test "keep arrays" do
rows = [
[1, 2, 3],
[5, 5, 5],
[5, 1, 2],
[2, 1, 2],
[1, 5, 2],
[2, 2, 1],
[1, 2, 5]
]

assert Strain.keep(rows, fn row -> 5 in row end) == [
[5, 5, 5],
[5, 1, 2],
[1, 5, 2],
[1, 2, 5]
]
end

# @tag :pending
end

# @tag :pending
assert Strain.discard([1, 2, 3], fn e -> e > 10 end) == [1, 2, 3]
end

# @tag :pending
test "discard first and last" do
assert Strain.discard([1, 2, 3], &is_odd?/1) == [2]
end

# @tag :pending
test "discard neither first nor last" do
assert Strain.discard([1, 2, 3, 4, 5], &is_even?/1) == [1, 3, 5]
end

# @tag :pending
words = ~w(apple zebra banana zombies cherimoya zelot)
assert Strain.discard(words, &String.starts_with?(&1, "z")) == ~w(apple banana cherimoya)
end

# @tag :pending
rows = [
[1, 2, 3],
[5, 5, 5],
[5, 1, 2],
[2, 1, 2],
[1, 5, 2],
[2, 2, 1],
[1, 2, 5]
]

assert Strain.discard(rows, fn row -> 5 in row end) == [[1, 2, 3], [2, 1, 2], [2, 2, 1]]
end
end``````