🎉 Exercism Research is now launched. Help Exercism, help science and have some fun at research.exercism.io 🎉
Avatar of sFrenkie

sFrenkie's solution

to Allergies in the Elixir Track

Published at Feb 12 2020 · 0 comments
Instructions
Test suite
Solution

Given a person's allergy score, determine whether or not they're allergic to a given item, and their full list of allergies.

An allergy test produces a single numeric score which contains the information about all the allergies the person has (that they were tested for).

The list of items (and their value) that were tested are:

  • eggs (1)
  • peanuts (2)
  • shellfish (4)
  • strawberries (8)
  • tomatoes (16)
  • chocolate (32)
  • pollen (64)
  • cats (128)

So if Tom is allergic to peanuts and chocolate, he gets a score of 34.

Now, given just that score of 34, your program should be able to say:

  • Whether Tom is allergic to any one of those allergens listed above.
  • All the allergens Tom is allergic to.

Note: a given score may include allergens not listed above (i.e. allergens that score 256, 512, 1024, etc.). Your program should ignore those components of the score. For example, if the allergy score is 257, your program should only report the eggs (1) allergy.

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

Jumpstart Lab Warm-up http://jumpstartlab.com

Submitting Incomplete Solutions

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

allergies_test.exs

defmodule AllergiesTest do
  use ExUnit.Case

  defp assert_is_a_set_containing(list, to_contain) do
    set = Enum.into(list, MapSet.new())

    same_contents =
      to_contain
      |> Enum.into(MapSet.new())
      |> MapSet.equal?(set)

    assert same_contents,
           "Expected a set with: #{inspect(to_contain)} got #{inspect(set |> MapSet.to_list())}"
  end

  describe "allergy list against expected allergens -" do
    # @tag :pending
    test "no allergies at all" do
      Allergies.list(0) |> assert_is_a_set_containing([])
    end

    @tag :pending
    test "allergic to just eggs" do
      Allergies.list(1) |> assert_is_a_set_containing(~w[eggs])
    end

    @tag :pending
    test "allergic to just peanuts" do
      Allergies.list(2) |> assert_is_a_set_containing(~w[peanuts])
    end

    @tag :pending
    test "allergic to just strawberries" do
      Allergies.list(8) |> assert_is_a_set_containing(~w[strawberries])
    end

    @tag :pending
    test "allergic to eggs and peanuts" do
      Allergies.list(3) |> assert_is_a_set_containing(~w[eggs peanuts])
    end

    @tag :pending
    test "allergic to more than eggs but not peanuts" do
      Allergies.list(5) |> assert_is_a_set_containing(~w[eggs shellfish])
    end

    @tag :pending
    test "allergic to lots of stuff" do
      Allergies.list(248)
      |> assert_is_a_set_containing(~w[strawberries tomatoes chocolate pollen cats])
    end

    @tag :pending
    test "allergic to everything" do
      Allergies.list(255)
      |> assert_is_a_set_containing(
        ~w[eggs peanuts shellfish strawberries tomatoes chocolate pollen cats]
      )
    end

    @tag :pending
    test "ignore non allergen score parts" do
      Allergies.list(509)
      |> assert_is_a_set_containing(
        ~w[eggs shellfish strawberries tomatoes chocolate pollen cats]
      )
    end
  end

  describe "score for egg allergies -" do
    @tag :pending
    test "not allergic to eggs" do
      refute Allergies.allergic_to?(0, "eggs")
    end

    @tag :pending
    test "is allergic to only eggs" do
      assert Allergies.allergic_to?(1, "eggs")
    end

    @tag :pending
    test "is allergic to eggs and something else" do
      assert Allergies.allergic_to?(3, "eggs")
    end

    @tag :pending
    test "is allergic to something, but not eggs" do
      refute Allergies.allergic_to?(2, "eggs")
    end

    @tag :pending
    test "is allergic to everything (including eggs)" do
      assert Allergies.allergic_to?(255, "eggs")
    end
  end

  describe "score for peanuts allergies -" do
    @tag :pending
    test "not allergic to peanuts" do
      refute Allergies.allergic_to?(0, "peanuts")
    end

    @tag :pending
    test "is allergic to only peanuts" do
      assert Allergies.allergic_to?(2, "peanuts")
    end

    @tag :pending
    test "is allergic to peanuts and something else" do
      assert Allergies.allergic_to?(7, "peanuts")
    end

    @tag :pending
    test "is allergic to something, but not peanuts" do
      refute Allergies.allergic_to?(5, "peanuts")
    end

    @tag :pending
    test "is allergic to everything (including peanuts)" do
      assert Allergies.allergic_to?(255, "peanuts")
    end
  end

  describe "score for shellfish allergies -" do
    @tag :pending
    test "not allergic to shellfish" do
      refute Allergies.allergic_to?(0, "shellfish")
    end

    @tag :pending
    test "is allergic to only shellfish" do
      assert Allergies.allergic_to?(4, "shellfish")
    end

    @tag :pending
    test "is allergic to shellfish and something else" do
      assert Allergies.allergic_to?(14, "shellfish")
    end

    @tag :pending
    test "is allergic to something, but not shellfish" do
      refute Allergies.allergic_to?(10, "shellfish")
    end

    @tag :pending
    test "is allergic to everything (including shellfish)" do
      assert Allergies.allergic_to?(255, "shellfish")
    end
  end

  describe "score for strawberries allergies -" do
    @tag :pending
    test "not allergic to strawberries" do
      refute Allergies.allergic_to?(0, "strawberries")
    end

    @tag :pending
    test "is allergic to only strawberries" do
      assert Allergies.allergic_to?(8, "strawberries")
    end

    @tag :pending
    test "is allergic to strawberries and something else" do
      assert Allergies.allergic_to?(28, "strawberries")
    end

    @tag :pending
    test "is allergic to something, but not strawberries" do
      refute Allergies.allergic_to?(20, "strawberries")
    end

    @tag :pending
    test "is allergic to everything (including strawberries)" do
      assert Allergies.allergic_to?(255, "strawberries")
    end
  end

  describe "score for tomatoes allergies -" do
    @tag :pending
    test "not allergic to tomatoes" do
      refute Allergies.allergic_to?(0, "tomatoes")
    end

    @tag :pending
    test "is allergic to only tomatoes" do
      assert Allergies.allergic_to?(16, "tomatoes")
    end

    @tag :pending
    test "is allergic to tomatoes and something else" do
      assert Allergies.allergic_to?(56, "tomatoes")
    end

    @tag :pending
    test "is allergic to something, but not tomatoes" do
      refute Allergies.allergic_to?(40, "tomatoes")
    end

    @tag :pending
    test "is allergic to everything (including tomatoes)" do
      assert Allergies.allergic_to?(255, "tomatoes")
    end
  end

  describe "score for chocolate allergies -" do
    @tag :pending
    test "not allergic to chocolate" do
      refute Allergies.allergic_to?(0, "chocolate")
    end

    @tag :pending
    test "is allergic to only chocolate" do
      assert Allergies.allergic_to?(32, "chocolate")
    end

    @tag :pending
    test "is allergic to chocolate and something else" do
      assert Allergies.allergic_to?(112, "chocolate")
    end

    @tag :pending
    test "is allergic to something, but not chocolate" do
      refute Allergies.allergic_to?(80, "chocolate")
    end

    @tag :pending
    test "is allergic to everything (including chocolate)" do
      assert Allergies.allergic_to?(255, "chocolate")
    end
  end

  describe "score for pollen allergies -" do
    @tag :pending
    test "not allergic to pollen" do
      refute Allergies.allergic_to?(0, "pollen")
    end

    @tag :pending
    test "is allergic to only pollen" do
      assert Allergies.allergic_to?(64, "pollen")
    end

    @tag :pending
    test "is allergic to pollen and something else" do
      assert Allergies.allergic_to?(224, "pollen")
    end

    @tag :pending
    test "is allergic to something, but not pollen" do
      refute Allergies.allergic_to?(160, "pollen")
    end

    @tag :pending
    test "is allergic to everything (including pollen)" do
      assert Allergies.allergic_to?(255, "pollen")
    end
  end

  describe "score for cats allergies -" do
    @tag :pending
    test "not allergic to cats" do
      refute Allergies.allergic_to?(0, "cats")
    end

    @tag :pending
    test "is allergic to only cats" do
      assert Allergies.allergic_to?(128, "cats")
    end

    @tag :pending
    test "is allergic to cats and something else" do
      assert Allergies.allergic_to?(192, "cats")
    end

    @tag :pending
    test "is allergic to something, but not cats" do
      refute Allergies.allergic_to?(64, "cats")
    end

    @tag :pending
    test "is allergic to everything (including cats)" do
      assert Allergies.allergic_to?(255, "cats")
    end
  end
end

test_helper.exs

ExUnit.start()
ExUnit.configure(exclude: :pending, trace: true)
defmodule Allergies do
  use Bitwise

  @doc """
  List the allergies for which the corresponding flag bit is true.
  """
  @spec list(non_neg_integer) :: [String.t()]
  def list(flags) do
    %{
      1 => "eggs",
      2 => "peanuts",
      4 => "shellfish",
      8 => "strawberries",
      16 => "tomatoes",
      32 => "chocolate",
      64 => "pollen",
      128 => "cats"
    }
    |> Enum.map(fn kw ->
      {bi, alergen} = kw

      cond do
        (flags &&& bi) > 0 -> alergen
        true -> nil
      end
    end)
    |> Enum.filter(fn x -> x != nil end)
  end

  @doc """
  Returns whether the corresponding flag bit in 'flags' is set for the item.
  """
  @spec allergic_to?(non_neg_integer, String.t()) :: boolean
  def allergic_to?(flags, item) do
    list(flags) |> Enum.any?(fn x -> x == item end)
  end
end

Community comments

Find this solution interesting? Ask the author a question to learn more.

What can you learn from this solution?

A huge amount can be learned from reading other people’s code. This is why we wanted to give exercism users the option of making their solutions public.

Here are some questions to help you reflect on this solution and learn the most from it.

  • What compromises have been made?
  • Are there new concepts here that you could read more about to improve your understanding?