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] > <mailto:[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] > <mailto:[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 > > <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 > > <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] > <mailto:[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] > <mailto:[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] > <mailto:[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/7EDB412A-077A-46F1-B44C-01C1B8D58E19%40a-corp.co.uk.
