Djordjenp

Djordjenp

Function gives "2nd argument: not valid character data" error

I am new to elixir and I am trying to make a recursive anonymous function, but for some reason my anynymous function that works on it’s own as expected, throws me “2nd argument: not valid character data (an iodata term)” error.

Here is code:

calcTip = fn bill ->
  if bill >= 50 and bill <= 300, do: bill * 0.15, else: bill * 0.2
end

bills = [ 22, 295, 176, 440, 37, 105, 10, 1100, 86, 52 ]

calcTipsAndTotals = fn index, tips, totals, recursiveFn ->
  case index < length(bills) do
    true ->
      new_tip = calcTip.(Enum.at(bills, index, 0))
      new_tips = tips ++ [new_tip]
      new_totals = totals ++ [Enum.at(new_tips, index) + Enum.at(bills, index)]
      recursiveFn.(index + 1, new_tips, new_totals, recursiveFn)
    false -> [tips, totals]
  end
end

IO.puts(
  calcTipsAndTotals.(0, [], [], calcTipsAndTotals)
)

Marked As Solved

Also Liked

al2o3cr

al2o3cr

Not directly related to your question, but a general tip for new Elixir devs: calling functions like length and Enum.at inside a loop should make you slightly worried about performance.

The reason is that both of those functions take time that’s proportional to the size of the input (bills here usually), unlike other languages where arrays can be accessed in a constant amount of time. This means that calculating something like length(bills) inside a recursion over bills will immediately be accidentally quadratic.

IMO a good general principle is to avoid using indexes as much as possible. For instance, in your code above, every call to Enum.at uses the same index so the whole thing can be rewritten as an Enum.map:

calcTip = fn bill ->
  if bill >= 50 and bill <= 300, do: bill * 0.15, else: bill * 0.2
end

bills = [ 22, 295, 176, 440, 37, 105, 10, 1100, 86, 52 ]

tips_and_totals_as_pairs =
  Enum.map(bills, fn bill ->
    tip = calcTip.(bill)
    {tip, tip + bill}
  end)

tips_and_totals = Enum.unzip(tips_and_totals_as_pairs)

or an alternate version with explicit recursion, if that’s a requirement:

calcTip = fn bill ->
  if bill >= 50 and bill <= 300, do: bill * 0.15, else: bill * 0.2
end

bills = [ 22, 295, 176, 440, 37, 105, 10, 1100, 86, 52 ]

calc_tips_and_totals = fn
  [bill | rest], tips, totals, recursive_fn ->
    tip = calcTip.(bill)
    calc_tips_and_totals(rest, [tip | tips], [tip + bill | totals], recursive_fn)
  [], tips, totals, _ ->
    [Enum.reverse(tips), Enum.reverse(totals)]
  end

tips_and_totals = calc_tups_and_totals(bills, [], [], calc_tips_and_totals)

Some general notes from the above:

  • to know when to stop, instead of checking length (which is expensive) this pattern-matches on [bill | rest] vs [] (which is super-cheap)
  • instead of appending to the end of lists with totals ++ [new_value], this adds to the beginning of the list (super-cheap again) and then reverses at the end. See the BEAM Efficiency Guide for some additional discussion on this.
  • both of the versions above produce separate lists for tips and totals, but you may want to consider keeping those things together either as a tuple (omit the Enum.unzip) or even a map/struct. That way the values for a particular bill are always in one place, instead of spread across multiple lists.

Where Next?

Popular in Questions Top

gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
quazar
How to set Jason to encode all fields in ecto schema, I don’t care about security and implementing only is taking long list of attributes...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
beno
I will often find my self writing things similar to: case some_value do nil -&gt; something() "" -&gt; something() _ -&gt; someth...
New
jc00ke
Expanding on this topic: https://forum.elixirforum.net/t/map-typespec-question/19217 Let’s say I have a map with required and optional k...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New

Other popular topics Top

srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call th...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 record...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 35421 110
New
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list....
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New

We're in Beta

About us Mission Statement