Flatten a list of nested maps in Elixir - functional-programming

I'm trying to flatten a list of nested maps, so that the output is list of maps which can then be inserted into a database table. The nested maps could contain a list of maps. In general a minimal example of the nested map would be something like this:
%{
a: "a",
b: "b",
c: [
%{
d: "d1",
e: [%{f: "f1", g: "g1"}],
h: "h1",
i: "i1"
},
%{
d: "d2",
e: [%{f: "f2", g: "g2"}],
h: "h2",
i: "i2"
}
]
}
The output I'd be looking for is:
[
%{f: "f1", g: "g1", d: "d1", h: "h1", i: "i1", b: "b", a: "a"},
%{f: "f2", g: "g2", d: "d2", h: "h2", i: "i2", b: "b", a: "a"}
]
The length of the list is equal to the number of "terminal" maps (ie. the f key in this example). Also you'll note that where the nesting takes place, c and e, those keys are not necessary and therefore are dropped.
I have tried to recurse over the map keys, but the issue I run into is that the output is always the length of then number of keys in the parent map.
Any help or ideas on how to approach this problem would be appreciated.
Thanks!

Since we need to literally fork traversing the level when we meet a list as a value, our best friend would be Task.async_stream/3. Once we are to perform it lazily, all the internal operations are also lazy, until we need to terminate the result to return it from flatten/1 (with Enum.to_list/1.)
defmodule Flat do
#spec flatten(map()) :: [map()]
def flatten(input),
do: input |> do_flatten([%{}]) |> Enum.to_list()
#doc "here we fork it in parallel and collect results"
defp do_flatten([%{}|_] = input, acc) do
input
|> Task.async_stream(&do_flatten(&1, acc))
|> Stream.flat_map(&elem(&1, 1))
end
#doc """
add `{key, value}` pairs to each list
in currently accumulated result
"""
defp do_flatten(%{} = input, acc) do
Stream.flat_map(acc, fn list ->
Enum.reduce(input, [list], &do_flatten(&1, &2))
end)
end
#doc "enumerable as value → go for it"
defp do_flatten({_k, v}, acc) when is_list(v) or is_map(v),
do: do_flatten(v, acc)
#doc "the leaf, add to all lists in the accumulator"
defp do_flatten({k, v}, acc),
do: Stream.map(acc, &Map.put(&1, k, v))
end
input = %{
a: "a", b: "b",
c: [
%{d: "d1", e: [%{f: "f1", g: "g1"}], h: "h1", i: "i1"},
%{d: "d2", e: [%{f: "f2", g: "g2"}], h: "h2", i: "i2"}]
}
Flat.flatten()
#⇒ [
# %{a: "a", b: "b", d: "d1", f: "f1", g: "g1", h: "h1", i: "i1"},
# %{a: "a", b: "b", d: "d2", f: "f2", g: "g2", h: "h2", i: "i2"}
# ]
Here is a blog post explaining this technique in details on the example of “Wolf, Goat, Cabbage” riddle.

Related

Which approach to find multiple totals more closely follows the functional programming paradigm?

Say you have an array of objects with the structure like {id: 1, type: 'A', value: 10} want to find the total of type A, type B, and type C.
It would be more efficient to initialize the total variables and then loop through the array once, adding the the total variables based on type, than to use a reduce function for each total, in effect looping over the array 3 times.
However, from what I understand from the functional programming paradigm, functions should not manipulate anything outside of it internal scope and functions should have just one purpose, so the latter approach would be preferred.
Approach 1: initialize a variable for each of the three types, loop once and add to each total based on type
Approach 2: use reduce function for each total type.
Which one is preferred?
You can use a single fold/reduce if you use a record containing the three values as the state e.g. in clojure:
(defn sum-inputs [inputs]
(reduce (fn [acc {:keys [type value]}]
(update acc (keyword type) + value))
{:A 0 :B 0 :C 0}
inputs))
then
(sum-inputs [{:id 1 :type "A" :value 10}
{:id 2 :type "B" :value 12}
{:id 3 :type "B" :value 7}
{:id 4 :type "C" :value 40}])
in Javascript it looks like you can use Array.reduce:
const input = [{id: 1, type: "A", value: 4}, {id: 2, type: "B", value: 3}, {id: 3, type: "B", value: 9}, {id: 4, type: "C", value: 2}]
input.reduce(function(acc, i) { acc[i.type] += i.value; return acc; }, {A: 0, B: 0, C: 0})
note this mutates the accumulator record in place.

Elixir: What does a multiple-generator list comprehension look like without the syntax sugar?

I'm trying to understand list comprehensions in Elixir.
The example I'm looking at is producing the permutations of a string from this answer.
def shuffle([], _), do: [[]]
def shuffle(_, 0), do: [[]]
def shuffle(list, i) do
for x <- list, y <- shuffle(list, i-1), do: [x|y]
end
How does this double-generator comprehension look when re-written without the comprehension? I made an attempt to implement the algorithm myself, but my implementation is appending to the list, rather than prepending as in the comprehension. I want to write the algorithm without the comprehension but with identical behaviour.
A comprehension without filters can be converted into a sequence of Enum.flat_map and Enum.map. Specifically, all but the last one will become flat_map and the last one will become map. Here's a translation of your code:
list
|> Enum.flat_map(fn x ->
shuffle(list, i - 1)
|> Enum.map(fn y ->
[x | y]
end)
end)
I tested with A.shuffle([1, 2, 3, 4, 5], 2) and the output looks identical to the original code in that question.
Running Dogbert's example with the flat_map replaced with map really helped me see what was going on:
iex(1)> Permute.shuffle(~w(A B C), 3)
[
[
["A", ["A", ["A"]], ["A", ["B"]], ["A", ["C"]]],
["A", ["B", ["A"]], ["B", ["B"]], ["B", ["C"]]],
["A", ["C", ["A"]], ["C", ["B"]], ["C", ["C"]]]
],
[
["B", ["A", ["A"]], ["A", ["B"]], ["A", ["C"]]],
["B", ["B", ["A"]], ["B", ["B"]], ["B", ["C"]]],
["B", ["C", ["A"]], ["C", ["B"]], ["C", ["C"]]]
],
[
["C", ["A", ["A"]], ["A", ["B"]], ["A", ["C"]]],
["C", ["B", ["A"]], ["B", ["B"]], ["B", ["C"]]],
["C", ["C", ["A"]], ["C", ["B"]], ["C", ["C"]]]
]
]

VisJS graph2d draw order / groupOrder

Is there an equivalent groupOrder option for graph2d as there is for the timeline widget? http://visjs.org/docs/timeline/#Configuration_Options
Looking to draw bar charts in a specific order to handle overlapping.
The group order is determined by the order how you add groups, unless you set the ids as integers.
This is a example where you set the ids with strings:
a = new vis.DataSet()
-> DataSet {_options: Object, _data: Object, length: 0, _fieldId: "id", _type: Object…}
a.add([{id:"b"}, {id: "a"}, {id: "c"}])
-> ["b", "a", "c"]
a.getIds()
-> ["b", "a", "c"]
But when you create a dataset where the ids are integers, it will sort the datagroups based on the integers:
b = new vis.DataSet()
-> DataSet {_options: Object, _data: Object, length: 0, _fieldId: "id", _type: Object…}
b.add([{id:2}, {id: 3}, {id: 1}])
-> [2, 3, 1]
b.getIds()
-> [1, 2, 3]
When you mix the integers and strings it will sort the integers first and then then leave the strings unsorted.
c = new vis.DataSet()
DataSet {_options: Object, _data: Object, length: 0, _fieldId: "id", _type: Object…}
c.add([{id:"b"}, {id: 2}, {id: "a"} , {id: 1}])
["b", 2, "a", 1]
c.getIds()
[1, 2, "b", "a"]

hjson: why does close brace have to be on a separate line?

This works: (update: but not as I was thinking! it actually sets b = "c, d: e")
a: [
{ b: c, d: e
}
]
and this works:
a: [
{ "b": "c", "d": "e" }
]
But this doesn't work. What about the hjson definition disallows the closing brace at the end of the line?
a: [
{ b: c, d: e }
]
Found ']' where a key name was expected
(check your syntax or use quotes if the key name
includes {}[],: or whitespace): line 3 column 1 (char 23)
In Hjson a string without quotes is terminated by the newline, so your closing brace gets eaten by the quoteless string.
When you write
{ b: c, d: e
}
you are saying, give me a string that contains "c, d: e".
You need to use either quotes
{ b: "c", d: "e" }
or
{
b: c
d: e
}

Coloring edges on graph in Wolfram Mathematica

Is it possible to color an edge of graph in Wolfram Mathematica using color function, that depends on coordinate on the edge? Like using ColorFunction option in Plot[].
I have a function specified on the edge of the graph, depending on coordinate at the edge.
Is it possible to paint a dencity of this function on the edge?
Thanks for responce.
PS: The first idea - to use Inset[] to plug in graphical colored object in EdgeRenderingFunction, but it seems to be quite unnatural. Is there any simpliar ways?
One way to use ColorFunction to color edges in a Graph is:
ClearAll[colorededge];
colorededge[pts_, colorfunc_: Function[{x, y}, ColorData["TemperatureMap"][y]]] :=
ListPlot[pts, Joined -> True, PlotStyle -> Thick, Axes -> False,
ColorFunction -> colorfunc, ColorFunctionScaling -> True];
edgshpfnc = (If[Last[#2] == "B", First#colorededge[#1],
First#colorededge[#1, Function[{x, y}, Blend[{Yellow, Red}, x]]]] &);
Graph[{"A" -> "B", "B" -> "C", "C" -> "A"},
VertexCoordinates -> {"A" -> {0, 0}, "B" -> {1, 1}, "C" -> {2, 0}},
EdgeShapeFunction -> edgshpfnc, VertexLabels -> "Name", ImagePadding -> 10]
gives
and
GraphPlot[{"A" -> "B", "B" -> "C", "C" -> "A"},
EdgeRenderingFunction -> edgshpfnc, VertexLabeling -> True]
gives

Resources