hakarabakara

hakarabakara

Replacing placeholders in a string from CSV values

Hi,

My application accepts a string input and dynamic values from an uploaded CSV file and should generate a response based on the csv.

The string has placeholders have corresponding values in the CSV under the headers.

I was wondering whether there is a more efficient way of reading the stream once to perform the whole operation as opposed to fetching columns which are in the first row then streaming through the file again replacing the occurrence of the header in the string.

First Post!

mindok

mindok

Hi,

It’s not quite clear to me what you’re trying to achieve. It might be useful to show a small example of the two inputs (string input and CSV file) and expected output.

Most Liked

amnu3387

amnu3387


template =
      "hi #name, you have scored #physics in physics, #chemistry in chemistry and #maths in maths"

input = """
    name,maths,chemistry,physics
    James Ortega,44,14,49
    Eula Garrett,26,74,16
    Emily Marsh,47,22,73
    Tommy Jenkins,37,54,55
    Ronnie Nash,54,04,31
"""

[headers] =
      input
      |> NimbleCSV.RFC4180.parse_string(skip_headers: false)
      |> Enum.take(1)

replacement_hs =
      headers
      |> Enum.map(fn header -> "#" <> header end)

input
|> NimbleCSV.RFC4180.parse_string(skip_headers: true)
|> Enum.map(fn row ->
      Enum.zip_reduce(row, replacement_hs, template, fn col_val, header, acc ->
        String.replace(acc, header, col_val)
    end)
end)

The only reasons to use NimbleCSV here is because sometimes there can be escaped separation characters and if you have a streamable source it’s just easier to get it working. Using @mindok example but with a string I would just map the headers into their replacement tokens ("#some_header") and then brute force the replacements unless it turned out to be slower than acceptable at which maybe compiling a custom regex could be faster.

Sebb

Sebb

Definitely use a CSV-parser. I just wanted to keep it simple.

I like your little template-engine, but maybe its not really beginner-friendly (and it doesn’t address the selection of the template by CSV header).

my solution with nimble

def load(csv) do
  [header | rows] = NimbleCSV.RFC4180.parse_string(csv, skip_headers: false)
  Enum.map(rows, fn row -> sentence(row, header) end)
end

defp sentence([name, email, balance], ["name", "email", "balance"]) do
  "#{name}, you owe us #{balance} this month. Please see your statement here #{email}"
end

defp sentence([other, header], ["other", "header"]) do
  "#{other}, ... #{header} ..."
end

@mnussbaumer’s solution is better because you don’t need to write code for the templates, could even put them in a json if you want. Just create a map header -> template from which to select the template you need.

Sebb

Sebb

Still not sure what you want, is it this?

test "sentences" do
  data = """
  name,email,balance
  Francis Waters,jolir@jalih.mz,$1810.08
  Ina Thomas,duzzigip@hizjos.cl,$5639.13
  George Cortez,siw@jijol.ma,$222.81
  Oscar Nguyen,ov@rici.nu,$7167.56
  Wayne Campbell,nad@tuj.jp,$964.14
  """
  
  rows = String.split(data, "\n")
  Enum.map(rows, fn line -> line |> String.split(",") |> sentence() end)
end


defp sentence([name, email, balance]) do
  "#{name}, you owe us #{balance} this month. Please see your statement here #{email}"
end
defp sentence(_), do: ""
["name, you owe us balance this month. Please see your statement here email",
 "Francis Waters, you owe us $1810.08 this month. Please see your statement here jolir@jalih.mz",
 "Ina Thomas, you owe us $5639.13 this month. Please see your statement here duzzigip@hizjos.cl",
 "George Cortez, you owe us $222.81 this month. Please see your statement here siw@jijol.ma",
 "Oscar Nguyen, you owe us $7167.56 this month. Please see your statement here ov@rici.nu",
 "Wayne Campbell, you owe us $964.14 this month. Please see your statement here nad@tuj.jp",
 ""]

Last Post!

l00ker

l00ker

Not necessarily. You can use a mix release which will include ERTS and the compiled Elixir app. You don’t need to have Erlang & Elixir installed on the production VM. That’s the real beauty of a mix release.


A little off topic but relevant to my suggestion:

The easiest way to do this is to install the lxd snap on your Ubuntu box and then launch a CentOS 7 container and shell into it.

Inside the CentOS 7 container install asdf, then the Erlang & Elixir asdf plugins. Then install all of the dependencies required to build Erlang (see the asdf erlang plugin repo).

After that install Erlang & Elixir via asdf, copy your mix project into the container and build the release. Upload the release the the production VM and run it.

It might sound like a bit much, but it’s actually not that complicated and once it’s done, you will then have the CentOS 7 container around to use for future releases as well as a playground to build and test anything else that you don’t want to install on the production CentOS server. I’ve been using LXC containers for all sorts of things like that for years now.

Where Next?

Popular in Questions Top

dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New
Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
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
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New

Other popular topics Top

hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" =&gt; #BSON.ObjectId&lt;58eb1a7a9ad169198c3dXXXX&gt;, "email" =&gt; ...
New
dogweather
I wrote this comment on r/haskell, and it’s not popular there. :wink: But I think I’m on to something… Haskell reminds me of Java, and e...
New
AstonJ
Seen any cool LiveView demos, sample apps or examples? Please post them here! :003:
New
sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
985 44778 311
New
AngeloChecked
What learn first? Rust or Elixir Hi Elixir community! I’m here because i want learn a new language. I’m a junior developer and mainly i ...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New

We're in Beta

About us Mission Statement