Enum.reduce() does not compose as well when you have conditional Map.put spread over multiple private functions.
On Fri, Jun 5, 2020 at 5:20 PM Adam Lancaster <[email protected]> wrote: > Thinking about it, can you solve the original problem with reduce? > > ```elixir > new_values = %{ > foo: 10, > bar: 20 > } > > Enum.reduce(map, %{}, fn > {key, nil}, acc -> Map.put(acc, key, Map.get(new_values, key)) > {key, value}, acc -> Map.put(acc, key, value) > end) > ``` > > Best > > Adam > > On 5 Jun 2020, at 15:10, Leandro Cesquini Pereira < > [email protected]> wrote: > > `maybe` is a common prefix for functions that may or may not do something, > eg: > > > https://github.com/elixir-lang/elixir/blob/779ccdb7564d587dfdadc1285a4dc57eb24768a1/lib/logger/lib/logger.ex#L1074 > > > https://github.com/elixir-lang/elixir/blob/779ccdb7564d587dfdadc1285a4dc57eb24768a1/lib/elixir/lib/file.ex#L1796 > > And so on... > > So I'd propose to call it `maybe_run`, just `maybe`, or something with > `maybe`. > > On Friday, June 5, 2020 at 9:48:56 AM UTC-4, Adam Lancaster wrote: >> >> We have something similar in our code base we called the Maybe.Pipe: ~> >> >> >> On 5 Jun 2020, at 14:41, Piotr Szmielew <[email protected]> wrote: >> >> maybe it should be called run_if? so, something like this: >> >> defmacro run_if(piped_value, condition, fun) do >> quote do >> if unquote(condition) do >> unquote(fun).(unquote(piped_value)) >> else >> unquote(piped_value) >> end >> end >> end >> >> and then used as: >> >> %{a: 1, b: 2} >> |> X.run_if(1 == 2, &Map.put(&1, :foo, "foo")) >> |> X.run_if(1 == 1, &Map.put(&1, :foo, "bar")) >> # => %{a: 1, b: 2, foo: "bar"} >> >> On Fri, Jun 5, 2020 at 3:27 PM José Valim <[email protected]> wrote: >> >>> Hi Riccardo, >>> >>> This is an interesting proposal! Unfortunately if the value you want to >>> compute is expensive, then you need to fallback to the usual `if` approach. >>> This is also specific to maps. It may be worth considering a more general >>> purpose mechanism, for example: >>> >>> map >>> |> update_if(foo != nil, &Map.put(&1, :foo, foo)) >>> |> update_if(bar != nil, &Map.put(&1, :bar, normalize_bar(bar))) >>> >>> Being in Kernel also allows us to write it as a macro, which can be >>> further optimized. Not sure if update_if is the best name though. >>> Suggestions and comparisons to other languages are welcome. >>> >>> On Fri, Jun 5, 2020 at 12:36 PM Riccardo Binetti <[email protected]> wrote: >>> >>>> Hi everybody, >>>> >>>> this is my first proposal so I hope I get the format right, I've >>>> already checked in the mailing list and I haven't found similar proposals. >>>> >>>> *Problem* >>>> >>>> When manually constructing a map using external options or the result >>>> of a pattern match on a struct, I often find myself in the situation where >>>> I have to add a value only if it is non-nil. >>>> >>>> The possible approaches available today are these >>>> >>>> >>>> *Using if* >>>> >>>> >>>> def some_fun(s) do >>>> MyStruct{ >>>> foo: foo, >>>> bar: bar, >>>> baz: baz >>>> } = s >>>> >>>> map = %{a: "hello", b: 42} >>>> >>>> map = >>>> if foo do >>>> Map.put(map, :foo, foo) >>>> else >>>> map >>>> end >>>> >>>> map = >>>> if bar do >>>> Map.put(map, :bar, bar) >>>> else >>>> map >>>> end >>>> >>>> if bar do >>>> Map.put(map, :baz, baz) >>>> else >>>> map >>>> end >>>> end >>>> >>>> >>>> This is the obvious solution, but it gets very verbose very quickly >>>> >>>> >>>> *Populating the map and then filtering* >>>> >>>> def some_fun(s) do >>>> MyStruct{ >>>> foo: foo, >>>> bar: bar, >>>> baz: baz >>>> } = s >>>> >>>> %{a: "hello", b: 42} >>>> |> Map.put(:foo, foo) >>>> |> Map.put(:bar, bar) >>>> |> Map.put(:baz, baz) >>>> |> Enum.filter(fn {_k, v} -> v end) >>>> |> Enum.into(%{}) >>>> end >>>> >>>> This is more concise, but it creates intermediate maps that are >>>> immediately thrown away. Moreover, if the initial map is very large, the >>>> filter operation is expensive since it traverses the whole map. >>>> >>>> >>>> *Implementing a maybe_put helper function* >>>> >>>> def some_fun(s) do >>>> MyStruct{ >>>> foo: foo, >>>> bar: bar, >>>> baz: baz >>>> } = s >>>> >>>> %{a: "hello", b: 42} >>>> |> maybe_put(:foo, foo) >>>> |> maybe_put(:bar, bar) >>>> |> maybe_put(:baz, baz) >>>> end >>>> >>>> defp maybe_put(map, _key, nil), do: map >>>> defp maybe_put(map, key, value), do: Map.put(map, key, value) >>>> >>>> This is the solution I end implementing almost always. It is present in >>>> multiple modules across the codebase I work on (and/or put in a generic >>>> Utils module), and talking with my colleagues I found out that is a utility >>>> function that they also have to implement often and it has turned up at >>>> least a couple times[1][2] also in the Elixir Forum. >>>> >>>> [1] >>>> https://elixirforum.com/t/elixir-way-to-conditionally-update-a-map/17952/21?u=rbino >>>> [2] >>>> https://elixirforum.com/t/is-there-way-to-create-map-and-conditionally-exclude-some-keys/23315/5?u=rbino >>>> >>>> This brings me to my proposal, which is a more generic version of >>>> maybe_put. >>>> >>>> *Proposal* >>>> >>>> Add Map.put_if/4 to the Map module. This is more generic (and imho >>>> clearer) than maybe_put and can be used to solve the same problem without >>>> creating intermediate results and allowing the code to be clear and >>>> concise. >>>> >>>> >>>> def some_fun(s) do >>>> MyStruct{ >>>> foo: foo, >>>> bar: bar, >>>> baz: baz >>>> } = s >>>> >>>> %{a: "hello", b: 42} >>>> |> Map.put_if(:foo, foo, foo != nil) >>>> |> Map.put_if(:bar, bar, bar != nil) >>>> |> Map.put_if(:baz, baz, baz != nil) >>>> end >>>> >>>> Let me know if you need any other information. >>>> -- >>>> Riccardo >>>> >>>> -- >>>> You received this message because you are subscribed to the Google >>>> Groups "elixir-lang-core" group. >>>> To unsubscribe from this group and stop receiving emails from it, send >>>> an email to [email protected]. >>>> To view this discussion on the web visit >>>> https://groups.google.com/d/msgid/elixir-lang-core/f513b901-fd93-4f28-88a2-f496a3c84a21o%40googlegroups.com >>>> <https://groups.google.com/d/msgid/elixir-lang-core/f513b901-fd93-4f28-88a2-f496a3c84a21o%40googlegroups.com?utm_medium=email&utm_source=footer> >>>> . >>>> >>> >>> -- >>> You received this message because you are subscribed to the Google >>> Groups "elixir-lang-core" group. >>> To unsubscribe from this group and stop receiving emails from it, send >>> an email to [email protected]. >>> To view this discussion on the web visit >>> https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4K_QVpD-eRG6JUOMYV5pVTLchpTgbO0fqN6pPCmuSL4NA%40mail.gmail.com >>> <https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4K_QVpD-eRG6JUOMYV5pVTLchpTgbO0fqN6pPCmuSL4NA%40mail.gmail.com?utm_medium=email&utm_source=footer> >>> . >>> >> >> -- >> You received this message because you are subscribed to the Google Groups >> "elixir-lang-core" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to [email protected]. >> To view this discussion on the web visit >> https://groups.google.com/d/msgid/elixir-lang-core/CANmTJ6thcegKJjb7aKPhtQoqfo5%3DCgJo2-KnORRg8B2UcEB%3D-A%40mail.gmail.com >> <https://groups.google.com/d/msgid/elixir-lang-core/CANmTJ6thcegKJjb7aKPhtQoqfo5%3DCgJo2-KnORRg8B2UcEB%3D-A%40mail.gmail.com?utm_medium=email&utm_source=footer> >> . >> >> >> > -- > You received this message because you are subscribed to the Google Groups > "elixir-lang-core" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to [email protected]. > To view this discussion on the web visit > https://groups.google.com/d/msgid/elixir-lang-core/45a9ec20-f997-4fdf-a4c0-ea88388a8fabo%40googlegroups.com > <https://groups.google.com/d/msgid/elixir-lang-core/45a9ec20-f997-4fdf-a4c0-ea88388a8fabo%40googlegroups.com?utm_medium=email&utm_source=footer> > . > > > -- > You received this message because you are subscribed to the Google Groups > "elixir-lang-core" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to [email protected]. > To view this discussion on the web visit > https://groups.google.com/d/msgid/elixir-lang-core/4F9BCFD1-91CB-4A45-B6C2-BC1E534889DF%40channelviewestates.co.uk > <https://groups.google.com/d/msgid/elixir-lang-core/4F9BCFD1-91CB-4A45-B6C2-BC1E534889DF%40channelviewestates.co.uk?utm_medium=email&utm_source=footer> > . > -- Best regards Alexei Sholik -- You received this message because you are subscribed to the Google Groups "elixir-lang-core" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAAPY6ePNqjxHozP%2B39GD2yoKihsM8EKudEbnCdRvfGGWqipMTg%40mail.gmail.com.
