#matching maps

16 messages · Page 1 of 1 (latest)

little igloo
#

I want to match on list of maps that looks like:

[
  %{
    predictions: [
      %{label: "neutral", score: 0.9985172152519226},
      %{label: "negative", score: 0.0010232925415039062},
      %{label: "positive", score: 4.594727361109108e-4}
    ]
  },...
]
ger_sentiments = Enum.map(ger_predictions, fn x -> SentimentScore.score(x.predictions) end)

To sum up the individual values. With a function that looks like:

defmodule SentimentScore do
  def score(prediction) do
    prediction
    |> Enum.map(fn p ->
      cond do
        p.label == "POS" -> p.score
        p.label == "positive" -> p.score
        p.label == "NEG" -> -p.score
        p.label == "negative" -> -p.score
        p.label == "NEU" -> 0
        p.label == "neutral" -> 0
      end
    end)
    |> Enum.sum()
  end
end

I can do as want. I just want it to look more like Elixir code and apply matching_

defmodule SentimentScore do
  def score(prediction) do
    prediction
    |> Enum.map(fn p ->
      cond do
        %{label: "POS"} = p -> p.score
        %{label: "positive"} = p -> p.score
        %{label: "NEG"} = p -> -p.score
        %{label: "negative"} = p -> -p.score
        %{label: "NEU"} = p -> 0
        %{label: "neutral"} = p -> 0
      end
    end)
    |> Enum.sum()
  end
end

But here I get a matching error:

(MatchError) no match of right hand side value: %{label: "neutral", score: 0.9985172152519226}

What is the errror I overlooked?

#

Error correction:

#
defmodule SentimentScore do
  def score(prediction) do
    prediction
    |> Enum.map(fn p ->
      cond do
        %{:label => "POS"} == p -> p.score
        %{:label => "positive"} == p -> p.score
        %{:label => "NEG"} == p -> -p.score
        %{:label => "negative"} == p -> -p.score
        %{:label => "NEU"} == p -> 0
        %{:label => "neutral"} == p -> 0
      end
    end)
    |> Enum.sum()
  end
end
#

returns the error: no cond clause evaluated to a truthy value

boreal pine
#

Seems like you might want case, not cond?

fallen basin
#

why not collapse all of the lists into a single structure? you can still pattern match against string-type or atom-type keys:

predictions: %{neutral: 0.9985172152519926, negative: 0.0010232925415039062}
steel vigil
#

like mentioned you're probably looking for case:

case p do
  %{label: l} when l in ["POS", "positive"] -> p.score
  %{label: l} when l in ["NEG", "negative"] -> -p.score
  _ -> 0
end
sand sparrow
#
iex(1)> a = %{a: 1, b: 2}
%{a: 1, b: 2}
iex(2)> b = %{a: 1}
%{a: 1}
iex(3)> a == b
false

Maps need to have exact keys and values to be equal.
As others said, you probably want case not cond. If you still want cond, you can use Kernel.match? like match?(p, %{label: "positive"}) as conditions of your cond.

I'm taking @steel vigil 's example, I think this is more "Elixir-y" but don't let these things get into your head too much.

case p do
  %{label: l, scope: scope} when l in ["POS", "positive"] -> score
  %{label: l, score: score} when l in ["NEG", "negative"] -> -score
  _ -> 0
end
#

Also, as to why you get a match error with your cond, cond doesn't do matching it just evaluates each clause sequentially and the first truthy one wins. In you example, %{label: "POS"} = p will certainly throw match error at some point and cond doesn't prevent bad matches.

little igloo
#

👍

midnight musk
#

Actually in this example you should use a multi-clause fn instead of a case

little igloo
#

is this a consensus? 😉

midnight musk
#

I'd argue it because it's more concise and more readable. A case within a function suggests that you're doing stuff outside the match in the function

burnt seal
#

It’s actually less concise. You duplicate the function name, thr def/do keywords, and the parens.

midnight musk
#

for some reason I remembered it as being in an fn

#

I think though in general, assuming the entire body is a case statement, making a multiheaded function is more idiomatic