<turbo-stream action="append" target="posts_list"><template>    <div class="postbit" id="199201" data-post-id="199201">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="Eiji" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/Eiji/120/36743_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  Eiji
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="tomr" data-post="1" data-topic="36594">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/tomr/48/21317_2.png" class="avatar"> tomr:</div>
<blockquote>
<pre data-code-wrap="elixir"><code class="lang-elixir">    project = Clients.get_project!(id)
    project = Map.put_new(project, :budget, total_budget(project.budget_items))
    project = Map.put_new(project, :budget_used, budget_used(project.work_items))
    project = Map.put_new(project, :budget_remaining, project.budget - project.budget_used)
</code></pre>
</blockquote>
</aside>
<p>In this specific case I would avoid using pipes as for example <code>reduce</code> on <code>fields</code> requires to write <code>Map.put/3</code> only once and therefore allows to focus on working on data. Also if you are updating map/struct key value then you can simply use <code>%{map | existing_key: new_value}</code> syntax which is short and nice.</p>
<p>Here are 2 examples … First goes short update map:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">project = Clients.get_project!(id)
budget = total_budget(project.budget_items)
budget_used = budget_used(project.work_items)
updated_project = %{project | budget: budget, budget_used, budget_remaining: budget - budget_used}
</code></pre>
<p>Second example shows usage of reduce:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule Project do
  defstruct [:budget, :budget_items, :budget_remaining, :budget_used, :work_items]

  def calculate(project) do
    fields = [:budget, :budget_used, :budget_remaining]
    Enum.reduce(fields, project, fn key, acc -&gt;
      value = Project.calculate(project, key)
      Map.put(project, key, value
    end)
    # short notation:
    # updated_project = Enum.reduce(fields, project, &amp;Map.put(&amp;2, &amp;1, Project.calculate(&amp;2, &amp;1))
  end

  def calculate(%{budget_items: items}, :budget), do: total_budget(items)
  def calculate(%{work_items: items}, :budget_used), do: budget_used(items)
  def calculate(%{budget: budget, budget_used: used}, :budget_remaining), do: budget - used
end

project = id |&gt; Clients.get_project!() |&gt; Project.calculate()
</code></pre> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="199201" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/piping-map-put-with-access-to-the-piped-value/36594/12">Post #11</a>
	                </div>
	            </div>
              <div id="likers-container-199201" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="199201"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #11"></div>
  </section>
</div>
    <div class="postbit" id="199202" data-post-id="199202">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="srowley" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/srowley/120/28720_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  srowley
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Doesn’t <code>%{map | key: value}</code> syntax overwrite existing keys? That would not match what the OP is doing (using <code>Map.put_new/3</code>).</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="199202" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/piping-map-put-with-access-to-the-piped-value/36594/13">Post #12</a>
	                </div>
	            </div>
              <div id="likers-container-199202" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="199202"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #12"></div>
  </section>
</div>
    <div class="postbit" id="199203" data-post-id="199203">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="eksperimental" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/eksperimental/120/1401_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  eksperimental
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Good ole reduce will do the job as well.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">def show(conn, %{"id" =&gt; id}) do
  project = Clients.get_project!(id)

  total = total_budget(project.budget_items)
  used = budget_used(project.work_items)

  values = [
    budget: total,
    budget_used: used,
    budget_remaining: total - used
  ]

  project = Enum.reduce(values, project, fn {k, v}, project -&gt; Map.put_new(project, k, v) end)

  changeset = Clients.change_budget_item(%BudgetItem{})
  render(conn, "show.html", project: project, changeset: changeset)
end
</code></pre> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="199203" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/piping-map-put-with-access-to-the-piped-value/36594/14">Post #13</a>
	                </div>
	            </div>
              <div id="likers-container-199203" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="199203"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #13"></div>
  </section>
</div>
    <div class="postbit" id="199204" data-post-id="199204">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="Eiji" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/Eiji/120/36743_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  Eiji
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="srowley" data-post="13" data-topic="36594">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/srowley/48/28720_2.png" class="avatar"> srowley:</div>
<blockquote>
<p>Doesn’t <code>%{map | key: value}</code> syntax overwrite existing keys?</p>
</blockquote>
</aside>
<p>correct</p>
<aside class="quote no-group" data-username="srowley" data-post="13" data-topic="36594">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/srowley/48/28720_2.png" class="avatar"> srowley:</div>
<blockquote>
<p>That would not match what the OP is doing (using <code>Map.put_new/3</code>).</p>
</blockquote>
</aside>
<p>Hmm … I’m not sure if assuming that only by it is good …</p>
<p>First of all it looks like a typical <code>Phoenix</code> app which is using <code>Ecto.Schema</code> (I guess that there are just <code>virtual</code> fields). The fields are generated only in the <code>controller</code> for <code>show</code> action. Of course there may be something in <code>Clients.get_project!/1</code>, but we can’t be sure about that. If that’s a typical <code>phoenix</code> context function then it’s just a simple query by <code>id</code>.</p>
<p>If we are using <code>struct</code> then <code>put_new/1</code> would have no affect as <code>project</code> would have <code>default</code> value (or <code>nil</code>) already. We know that those fields are generated based on other fields (imagine <code>full_name = "#{first_name} #{last_name}"</code>. In most cases such fields could be update (or more precisely re-generated) several times and therefore more interesting here is if related fields are changing.</p>
<p>Finally <code>Map.put_new/3</code> is not good for such usage. Look that <code>total_budget/1</code> and <code>budget_used/1</code> functions would be called regardless if there would be already value or not - in many cases it causes many problems. When I see such mistakes I just can’t assume such things and I’m simply providing a solution for most typical cases.</p>
<p>The best way is to provide sample input and output right after question, so our examples could be tested properly. Right now we <code>guess</code> that <code>x</code> solution is good or not. That’s said op asked more about code organization than a typical solution. I think that my examples are helpful also in this part.</p>
<p>Regarding my solution it’s really easy to update it by just wrapping <code>do … end</code> block inside an <code>if</code> with simple condition using <code>Map.has_key?/2</code> …</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule Project do
  defstruct [:budget, :budget_items, :budget_remaining, :budget_used, :work_items]

  def calculate(project) do
    fields = [:budget, :budget_used, :budget_remaining]
    Enum.reduce(fields, project, fn key, acc -&gt;
      if Map.has_key?(project, key) do
        project
      else
        value = Project.calculate(project, key)
        Map.put(project, key, value
      end
    end)
    # short notation:
    # updated_project = Enum.reduce(fields, project, &amp;Map.put(&amp;2, &amp;1, Project.calculate(&amp;2, &amp;1))
  end

  def calculate(%{budget_items: items}, :budget), do: total_budget(items)
  def calculate(%{work_items: items}, :budget_used), do: budget_used(items)
  def calculate(%{budget: budget, budget_used: used}, :budget_remaining), do: budget - used
end

project = id |&gt; Clients.get_project!() |&gt; Project.calculate()
</code></pre>
<p>Look that my solution is really easy to change.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="199204" data-batch-url="/posts/batch_likers">
                        3
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/piping-map-put-with-access-to-the-piped-value/36594/15">Post #14</a>
	                </div>
	            </div>
              <div id="likers-container-199204" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="199204"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #14"></div>
  </section>
</div>
    <div class="postbit" id="199208" data-post-id="199208">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="srowley" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/srowley/120/28720_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  srowley
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Yep!</p>
<p>I think I am giving people the impression that I don’t think these alternatives are helpful. They obviously all are! I just thought it would be good to note when an alternative doesn’t actually do what the original code did.</p>
<p>I agree that everything about the original code snippet feels like, “I am getting an Ecto struct, doing some stuff to it in a Phoenix controller or context, and then sending it to a view/template.” It follows that using <code>Map.put_new/3</code> will not have any effect on the struct being passed to it for exactly the reasons you note. We agree that trying to replicate such behavior via other means is not a worthy goal.</p>
<p>If <code>project</code> is an Ecto struct, I would just calculate the three <code>budget</code> values and use <code>%StructName{map | key: value}</code> syntax (exactly like your first example). You could also use <code>Kernel.struct!/2</code>, but I don’t know of any reason to prefer one over the other in this case.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="199208" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/piping-map-put-with-access-to-the-piped-value/36594/16">Post #15</a>
	                </div>
	            </div>
              <div id="likers-container-199208" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="199208"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-last-post cat-last-post" title="Last post!"></div>
  </section>
</div>
</template></turbo-stream><turbo-stream action="replace" target="load-more-container"><template><div id="load-more-container" class="load-more-container">
    <span class="all-loaded">— All posts loaded —</span>
</div></template></turbo-stream>