in a nested map find key and update its value - recursion

find key : sum in nested map and update its value to : bill * 100 + : coins
Need to pass test1
test "test1" do
assert BcToInt.data(%{
count: 3,
sum: %{bills: 1, coins: 99},
tax: %{count: 3, sum: %{bills: 1, coins: 1}}
}) ==
%{count: 3, sum: 199, tax: %{count: 3, sum: 101}}
end
I tried to do it using Map_replace() and checking value for nested map using is_map and call function again if true but how to get end result
def data(xmap) do
Enum.map(xmap, fn {_key, value} ->
keyy = :sum
aa = List.first(Map.values(xmap[keyy])) * 100 + List.last(Map.values(xmap[keyy]))
if Map.has_key?(xmap, keyy) do
Map.replace(xmap, keyy, aa)
if is_map(value) do
data1(value)
end
end
end)
end

Here's a version without using external libraries:
def data(map) do
map =
case map[:sum] do
%{bills: bills, coins: coins} -> %{map | sum: bills * 100 + coins}
_ -> map
end
Map.new(map, fn
{k, v} when is_map(v) -> {k, data(v)}
entry -> entry
end)
end
Usage:
iex(1)> data = ...
%{
count: 3,
sum: %{bills: 1, coins: 99},
tax: %{count: 3, sum: %{bills: 1, coins: 1}}
}
iex(2)> BcToInt.data(data)
%{count: 3, sum: 199, tax: %{count: 3, sum: 101}}

With a help of iteraptor library:
Mix.install([:iteraptor])
Iteraptor.map(data, fn
{_k, %{bills: bills, coins: coins}} -> bills * 100 + coins
# Is not `bill:` a typo?
{_k, %{bill: bills, coins: coins}} -> bills * 100 + coins
other -> other
end, yield: :all)
#⇒ %{count: 3, sum: 199, tax: %{count: 3, sum: 101}}

Your implementation correctly uses recursion to look into nested data structures. It looks like it's trying to use Map.replace/3 to try to modify the data structure in place, though. Elixir only has immutable data structures, so you need to construct a new map from the input rather than trying to update it in place.
I might implement the recursion here using pattern matching:
def data(%{bills: bills, coins: coins}) do
bills * 100 + coins
end
def data(map) when is_map(map) do
Map.new(map, fn {k, v} -> {k, data(v)} end)
end
def data(any) do
any
end
With this setup, if data/1 is called with a map with the :bills and :coins keys (not necessarily in a field named :sum) it adds them together; on any other map, it recurses through the values preserving the keys; and on any other value it returns the original value as-is.

def data(xmap) do
keyy = :sum
aa = List.first(Map.values(xmap[keyy])) * 100 +
List.last(Map.values(xmap[keyy]))
Map.new(xmap, fn
{k, _v} when k == keyy -> {k, aa}
{k, v} when is_map(v) -> {k, data(v)}
{k, v} -> {k, v}
end)
end

Related

Does there exist an iterative implementation of fibonacci that is not a tabulation?

All the iterative implementations that I have found seem to necessarily be using a tabulation method.
Is dynamic programming an alternative only to recursion, while it is a mandatory solution for iteration?
An iterative implementation of fibonacci that does not use tabulation, memoization, or a swap variable – this code from my answer here on getting hung-up learning functional style.
const append = (xs, x) =>
xs.concat ([x])
const fibseq = n => {
let seq = []
let a = 0
let b = 1
while (n >= 0) {
n = n - 1
seq = append (seq, a)
a = a + b
b = a - b
}
return seq
}
console.log (fibseq (500))
// [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ... ]
And remember, procedure differs from process – ie, recursive procedures can spawn iterative processes. Below, even though fibseq is defined recursively, the process that it spawns is iterative – again, no tabulation, memoization, or any other made-up terms going on
const recur = (...values) =>
({ type: recur, values })
const loop = f =>
{
let acc = f ()
while (acc && acc.type === recur)
acc = f (...acc.values)
return acc
}
const fibseq = x =>
loop ((n = x, seq = [], a = 0, b = 1) =>
n === 0
? seq.concat ([a])
: recur (n - 1, seq.concat ([a]), a + b, a))
console.time ('loop/recur')
console.log (fibseq (500))
console.timeEnd ('loop/recur')
// [ 0,
// 1,
// 1,
// 2,
// 3,
// 5,
// 8,
// 13,
// 21,
// 34,
// ... 490 more items ]
// loop/recur: 5ms
There is a definition of Fibonacci numbers as a sum of binomial coefficients, which themselves can be calculated iteratively, as a representation of all the compositions of (n-1) from 1s and 2s.
In Haskell, we could write:
-- https://rosettacode.org/wiki/Evaluate_binomial_coefficients#Haskell
Prelude> choose n k = product [k+1..n] `div` product [1..n-k]
Prelude> fib n = sum [(n-k-1) `choose` k | k <- [0..(n-1) `div` 2]]
Prelude> fib 100
354224848179261915075

Compare and reduce pairs in two lists

Given Alice's triplet and Bob's triplet (lists), I need to compare each element so if alice_triplet[i] > bob_triplet[i], Alice's score is incremented by one, and vice versa.
I have this code:
def main do
alice_triplet = [5, 6, 7]
bob_triplet = [3, 6, 10]
alice_score = 0
bob_score = 0
Enum.zip(alice_triplet, bob_triplet)
|> Enum.each(fn
tuple when elem(tuple, 0) > elem(tuple, 1) -> alice_score = alice_score + 1
tuple when elem(tuple, 1) > elem(tuple, 0) -> bob_score = bob_score + 1
_ -> nil end)
IO.puts alice_score
IO.puts bob_score
end
But, the output is:
0
0
Why? I think it is about the variable scope because I'm getting this warning:
warning: variable "alice_score" is unused solution.ex:12
warning: variable "bob_score" is unused solution.ex:13
Is there a "more functional" way to do this? I'm learning Elixir (and FP in general, actually), so any advice will be appreciated.
The statement alice_score = alice_score + 1 does not modify the outer alice_score, it creates a new local alice_score with the value set to the outer value + 1. This has been covered in many answers. The solution almost always is to use Enum.reduce/3 with the state you need to change used as the accumulator.
Here's how that can be applied to your code:
alice_triplet = [5, 6, 7]
bob_triplet = [3, 6, 10]
{alice_score, bob_score} = Enum.zip(alice_triplet, bob_triplet) |>
Enum.reduce({0, 0}, fn
tuple, {as, bs} when elem(tuple, 0) > elem(tuple, 1) -> {as + 1, bs}
tuple, {as, bs} when elem(tuple, 1) > elem(tuple, 0) -> {as, bs + 1}
_, {as, bs} -> {as, bs}
end)
IO.puts alice_score
IO.puts bob_score
You can also simplify the code using pattern matching instead of elem/2 (elem/2 is rarely used in idiomatic Elixir code):
alice_triplet = [5, 6, 7]
bob_triplet = [3, 6, 10]
{alice_score, bob_score} = Enum.zip(alice_triplet, bob_triplet) |>
Enum.reduce({0, 0}, fn
{a, b}, {as, bs} when a > b -> {as + 1, bs}
{a, b}, {as, bs} when b > a -> {as, bs + 1}
_, {as, bs} -> {as, bs}
end)
IO.puts alice_score
IO.puts bob_score
The output in both cases is
1
1

Elixir: Using variables in case statement

I've got this case statement which is giving an error 'variable constant1 is unused'. It seems to be ignoring the variables and returning the top line, so the variables obviously haven't got scope. If I replace the constant with a number 1 then it works. What is the best way of doing this in Elixir?
defmodule Main
do
def constant1, do: 1
def constant2, do: 1
def constant3, do: 1
x = 1
y = 0
z = 0
{a, b, c, d} =
case {x, y, z} do
{constant1, constant2, constant3} -> {1, 2, 3, 4}
{constant1, constant2, _} -> {5, 6, 7, 8}
{constant1, _, _} -> {9, 10, 11, 12}
{_, _, _} -> {13, 14, 15, 16}
end
IO.inspect {a, b, c, d}
end
Here is the output:
warning: variable constant1 is unused
Untitled 9:15
{1, 2, 3, 4}
Changing the constants to variables also doesn't work.
You have defined constant1 being a function. When you try to use it in pattern matching, Elixir expects the variable to be there and you’ve got an error. One can’t pattern match to functions.
What you’ve wanted is likely:
defmodule Main do
constant1 = 1
constant2 = 1
constant3 = 1
x = 1
y = 0
z = 0
{a, b, c, d} =
case {x, y, z} do
{^constant1, ^constant2, ^constant3} -> {1, 2, 3, 4}
{^constant1, ^constant2, _} -> {5, 6, 7, 8}
{^constant1, _, _} -> {9, 10, 11, 12}
{_, _, _} -> {13, 14, 15, 16}
end
IO.inspect {a, b, c, d}
end
#⇒ { 9, 10, 11, 12 }
Also, please remember that to pattern match to already defined value, one should use the pin operator ^ in front of matcher, otherwise the code
a = 42
case x do
a -> ...
end
will overwrite the value of a, setting it to the value of x (in the scope of case block, outside of case a will remain 42.) With ^, the code below will match if and only x == 42:
a = 42
case x do
^a -> ...
end
Answering the subsequent questions about “how to,” “can I use globals,” etc.
Elixir (as all known functional languages) has no notion of “global,” since everything is immutable from the outside point of view. True constants are being implemented as macros. Macros are compiled during the compilation stage to AST and therefore might be used as constants inside match:
defmodule Constants do
defmacro constant1 do
quote do: 1
end
defmacro constant2 do
quote do: 1
end
defmacro constant3 do
quote do: 1
end
end
defmodule Main do
require Constants
x = 1
y = 0
z = 0
{a, b, c, d} =·
case {x, y, z} do
{Constants.constant1, Constants.constant2, Constants.constant3} -> {1, 2, 3, 4}
{Constants.constant1, Constants.constant2, _} -> {5, 6, 7, 8}
{Constants.constant1, _, _} -> {9, 10, 11, 12}
{_, _, _} -> {13, 14, 15, 16}
end
IO.inspect {a, b, c, d}
end
The above works, because after compilation there is no Constants.constant1 anymore in Main code: there are real values: 1s and the code being run is:
case {x, y, z} do
{1, 1, 1} -> {1, 2, 3, 4}
{1, 1, _} -> {5, 6, 7, 8}
{1, _, _} -> {9, 10, 11, 12}
{_, _, _} -> {13, 14, 15, 16}
end
Hope it helps.

How to change all the values in an Elixir map

I see that there's an update in the Dict module, but what about an update_all method that changes all values?
I tried doing this with Enum.map but the type changed:
iex(6)> Enum.map(%{:a => 2}, fn {k, v} -> {k, v + 1} end)
[a: 3]
You could pipe to Enum.into(%{}) or use a for comprehension, i.e.:
iex> for {k, v} <- %{a: 1, b: 2}, into: %{}, do: {k, v + 1}
%{a: 2, b: 3}
You can also do:
iex> Map.new(%{:a => 2}, fn {k, v} -> {k, v + 1} end)
%{:a => 3}
But feel like there should be something in the standard library to make this easier (Map.??(%{:a => 2}, &(&1 + 1))).
Here's one idea:
def update_map map, [head|tail], func do
update_map(
Dict.update(map, head, :unknown, func),
tail,
func
)
end
def update_map map, [], _ do
map
end
Then to call it:
iex(1)> d = %{:a => 1, :b => 2, :c => 3}
%{a: 1, b: 2, c: 3}
iex(2)> update_map(d, Dict.keys(d), fn v -> v + 1 end)
%{a: 2, b: 3, c: 4}
Let me add Enum.into into the mix
headers
|> Enum.group_by(fn {k, _v} -> k end, fn {_k, v} -> v end)
|> Enum.into(%{}, fn {k, v} -> {k, Enum.join(v, ", ")} end)
This turns:
[{"cookie", "a"}, {"cookie", "b"}] into %{"cookie", "a, b"}

in F#, How do you merge 2 Collections.Map instances?

I am trying to merge two Maps, but there is no built in method for joining Collections. So how do you do it?
You can implement this using Map.fold and Map.add, since add is actually add/replace:
let map1 = Map.ofList [ 1, "one"; 2, "two"; 3, "three" ]
let map2 = Map.ofList [ 2, "two"; 3, "oranges"; 4, "four" ]
let newMap = Map.fold (fun acc key value -> Map.add key value acc) map1 map2
printfn "%A" newMap
Probably the reason merge isn't provided out of the box is that you need to deal with key conflicts. In this simple merge algorithm we simple take the key value pair from the second map, this may not be the behaviour you want.
An alternative way is this:
let merge (a : Map<'a, 'b>) (b : Map<'a, 'b>) (f : 'a -> 'b * 'b -> 'b) =
Map.fold (fun s k v ->
match Map.tryFind k s with
| Some v' -> Map.add k (f k (v, v')) s
| None -> Map.add k v s) a b
It lets you decide on what value you want if there is duplicate keys.
Example:
let a = Map([1,11;2,21;3,31;])
let b = Map([3,32; 4,41;5,51;6,61;])
merge a b (fun k (v, v') -> v + v');;
//Result
val it : Map<int,int> =
map [(1, 11); (2, 21); (3, 63); (4, 41); (5, 51); (6, 61)]
Notice that the key 3 is different.
Define the following function:
let join (p:Map<'a,'b>) (q:Map<'a,'b>) =
Map(Seq.concat [ (Map.toSeq p) ; (Map.toSeq q) ])
example:
let a = Map([1,11;2,21;3,31;])
let b = Map([3,32; 4,41;5,51;6,61;])
let c = join a b
and the result:
val c : Map<int,int> =
map [(1, 11); (2, 21); (3, 32); (4, 41); (5, 51); (6, 61)]
If you prefer using function composition you could define a join function like this:
let join (m1:Map<'a,'b>) (m2:Map<'a,'b>) =
Map.foldBack Map.add m2 m1
Given that m1 and m2 are Maps you would then use it like this:
m3 = join m1 m2
Some caveats:
Keys in m2 will overwrite those in m1
As others have noted it's faster to let the smaller map be the second argument as it will result in fewer insertions

Resources