Beginner in Elixir : parse CSV file

Hello I'm a beginner in Elixir and I want to parse and stock a CSV file in an Elixir object.

But it's display that:

** (FunctionClauseError) no function clause matching in anonymous fn/1 in The following arguments were given to anonymous fn/1 in # 1 ["41", "5", "59", "N", "80", "39", "0", "W", "Youngstown", "OH"] anonymous fn/1 in (elixir 1.10.3) lib/stream.ex:482: anonymous fn/4 in (elixir 1.10.3) lib/stream.ex:1449: Stream.do_element_resource/6 (elixir 1.10.3) lib/stream.ex:1609: Enumerable.Stream.do_each/4 (elixir 1.10.3) lib/enum.ex:959: (mix 1.10.3) lib/mix/task.ex:330: Mix.Task.run_task/3 (mix 1.10.3) lib/mix/cli.ex:82: Mix.CLI.run_task/2

Here my code:

defmodule Siren do def parseCSV do IO.puts("Let's parse CSV file...") File.stream!("../name.csv") |> Stream.map(&String.trim(&1)) |> Stream.map(&String.split(&1, ",")) |> Stream.filter(fn ["LatD" | _] -> false end) |> Enum.find(fn State -> String [LatD, LatM, LatS, NS, LonD, LonM, LonS, EW, City, State] -> IO.puts("find -> #{State}") true end) end
end

And the csv file:

LatD,LatM,LatS,NS,LonD,LonM,LonS,EW,City,State
41,5,59,N,80,39,0,W,Youngstown,OH
42,52,48,N,97,23,23,W,Yankton,SD
46,35,59,N,120,30,36,W,Yakima,WA
42,16,12,N,71,48,0,W,Worcester,MA
43,37,48,N,89,46,11,W,WisconsinDells,WI
36,5,59,N,80,15,0,W,Winston-Salem,NC
49,52,48,N,97,9,0,W,Winnipeg,MB
39,11,23,N,78,9,36,W,Winchester,VA
34,14,24,N,77,55,11,W,Wilmington,NC
39,45,0,N,75,33,0,W,Wilmington,DE
48,9,0,N,103,37,12,W,Williston,ND
41,15,0,N,77,0,0,W,Williamsport,PA
37,40,48,N,82,16,47,W,Williamson,WV
33,54,0,N,98,29,23,W,WichitaFalls,TX
37,41,23,N,97,20,23,W,Wichita,KS
40,4,11,N,80,43,12,W,Wheeling,WV
26,43,11,N,80,3,0,W,WestPalmBeach,FL
47,25,11,N,120,19,11,W,Wenatchee,WA
41,25,11,N,122,23,23,W,Weed,CA

2 Answers

The first issue is here:

|> Stream.filter(fn ["LatD" | _] -> false
end)

all the lines should pass this and the only first one matches the given clauses. This would fix the issue

|> Stream.filter(fn ["LatD" | _] -> false _ -> true
end)

or

|> Stream.reject(&match?(["LatD" | _], &1))

Enum.find(fn State -> String after looks unclear and would be surely the next issue. I failed to understand what have you tried to achieve here.


The general advice would be: don’t reinvent the wheel and use NimbleCSV written by José Valim to parse CSVs, because there are lot of corner cases (like commas inside quotes in any field etc,) handled properly in the aforementioned library.

1

Aleksei Matiushkin gave you the right answer but also you have this function:

fn State -> String [LatD, LatM, LatS, NS, LonD, LonM, LonS, EW, City, State] -> IO.puts("find -> #{State}") true
end

It accepts two possible values, either State which is an atom, or a list of 10 specific atoms.

What you want to do is use variables, and variables in Elixir start with a lowercase letter or an underscore if it has to be ignored.

fn state -> String [latd, latm, lats, ns, lond, lonm, lons, ew, city, state] -> IO.puts("find -> #{state}") true
end

But in this case, the first clause of the function will always match anything because it acts like a catch-all clause.

What you probably want is:

fn [_latd, _latm, _lats, _ns, _lond, _lonm, _lons, _ew, _city, state] -> IO.puts("find -> #{state}") # here decide if you want to return true or false, # for instance `state == NC` true
end
1

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

You Might Also Like