Oh, nice! And…it lets me flip the arguments to Enum.zip around.
Stream.zip([1, 2, 3])
takes the list , pairs it with the first item from the enumerable in its argument and passes on `{[“first”, “line”], 1}
Okay, I see how Stream.zip is actually streaming something now, but the zipping is counter intuitive to how Enum.zip works. Stream.zip could still stream a result if it acted like Enum.zip, but I guess you can still get the Enum.zip result with your solution, and you can get the actual Stream.zip result as well.
Here’s my actual code, which reads a csv file. Any tips (besides using a csv library)?
example csv file:
id,ship_to,net_amount
123,:NC,100.00
124,:OK,50.00
125,:TX,30.00
126,:CA,100.00
defmodule Parse do
def orders(path) do
io = File.open!(path, [:utf8, :read])
io
|> get_headers
|> Enum.map(&String.to_atom/1)
|> Stream.cycle
|> get_rows(io)
end
def get_headers(io) do
io
|> IO.read(:line)
|> String.trim_trailing
|> String.split(",")
end
def get_rows(header_cycle, io) do
IO.stream(io, :line)
|> Stream.map(&String.trim_trailing/1)
|> Stream.map(&String.split(&1, ",") )
|> Stream.map(&_convert_values/1)
|> Stream.map(&Enum.zip header_cycle, &1)
|> Enum.to_list
end
defp _convert_values([id, state, net]) do
id = String.to_integer(id)
state = String.trim_leading(state, ":")
|> String.to_atom
net = String.to_float(net)
[id, state, net]
end
end
I find this particular bit awkward:
def orders(path) do
io = File.open!(path, [:utf8, :read])
io
|> get_headers
|> Enum.map(&String.to_atom/1)
|> Stream.cycle
|> get_rows(io)
end
It feels tortured to have to create the io variable so that I can pass io to get_rows()
as well as get_header()
.
Also, is there a better way to convert a string like “:NC” to an atom?
Edit: I like this version better, although orders()
still feels a little funky:
defmodule Parse do
def orders(path) do
io = File.open!(path, [:utf8, :read])
io
|> get_headers
|> get_rows(io)
# io
# |> get_headers
# |> Enum.map(&String.to_atom/1)
# |> Stream.cycle
# |> get_rows(io)
end
def get_headers(io) do
io
|> IO.read(:line)
|> String.trim_trailing
|> String.split(",")
|> Enum.map(&String.to_atom/1)
|> Stream.cycle
end
def get_rows(header_cycle, io) do
IO.stream(io, :line)
|> Stream.map(&String.trim_trailing/1)
|> Stream.map(&String.split(&1, ",") )
|> Stream.map(&_convert_values/1)
|> Stream.map(&Enum.zip header_cycle, &1)
|> Enum.to_list
end
defp _convert_values([id, state, net]) do
id = String.to_integer(id)
state = String.trim_leading(state, ":")
|> String.to_atom
net = String.to_float(net)
[id, state, net]
end
end
Alternative for orders()
:
def orders(path) do
io = File.open!(path, [:utf8, :read])
headers = get_headers(io)
make_rows(io, headers)
end
What’s the fastest way to get all the lines from an io device in a list?