Forgive me because I’m not 100% sure what you mean, but you can do this:

def update_nil_values(map, new_values, into \\ %{}) do
   Enum.reduce(map, into,  fn
     {key, nil}, acc -> Map.put(acc, key, Map.get(new_values, key))
     {key, value}, acc -> Map.put(acc, key, value)
  end)
end





%MyStruct{a: nil, b: 10, c: nil}
|> update_nil_values(%{a: 20}, %MyStruct{})
|> other_stuff()
|> update_nil_values(%{c: 50}, %MyStruct{})



On Friday, 5 June 2020 15:26:36 UTC+1, alco wrote:
>
> 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] <javascript:>> 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] 
>> <javascript:>> 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] <javascript:>.
>> 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] <javascript:>.
>> 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/ca5bc645-0067-4917-9e23-f175c55b7925o%40googlegroups.com.

Reply via email to