I just want to know if there's a clean way to get the "other" element from a map in Elixir. By "other" I mean a second key-value pair, whose key I don't know.
Example: %{success: boolean, other => value}
This is the best I could come up with:
case map do
%{success: true} ->
other = map |> Map.delete(:success) |> Map.values |> List.first
# Do something with other
%{success: false} ->
error = map |> Map.delete(:success) |> Map.values |> List.first
# Do something with error
end
There's Map.pop/3 function, which accepts map and a key and returns a tuple with the value and a map without the key:
Map.pop %{ a: 1, b: 2 }, :a
# => {1, %{b: 2}}
and will refactor your code into something like:
case Map.pop(map, :success) do
{true, other_map} ->
other = other_map |> Map.values |> List.first
# Do something with other
{false, other_map} ->
error = other_map |> Map.values |> List.first
# Do something with error
end
I would go with old good Enum.reduce/3:
Enum.reduce %{success: true, foo: 42}, %{state: nil, map: %{}}, fn
{:success, value}, acc -> %{acc | state: value}
{key, value}, acc -> %{acc | map: Map.put(acc.map, key, value)}
end
#⇒ %{map: %{foo: 42}, state: true}
Now you might do whatever is needed without code duplication. Actually, the tuple is fine for collecting the result:
{success, map} =
Enum.reduce %{success: true, foo: 42}, {nil, %{}}, fn
{:success, value}, {_, acc} -> {value, acc}
{key, value}, {state, acc} -> {state, Map.put(acc, key, value)}
end
#⇒ {true, %{foo: 42}}
iex(9)> map = %{:success => true, {1,2,3} => 10}
%{:success => true, {1, 2, 3} => 10}
iex(10)> List.first(for {key, val} <- map, key != :success, do: val)
10
Related
I did this task using iteraptor libary, how can i do it using only elixir fuctionc not external libraries for example using get_in/2
defmodule MapanalizTest do
use ExUnit.Case
doctest Mapanaliz
test "all_path_list(prev_result, xmap, prev_path)" do
assert Mapanaliz.all_path_list(%{}, []) == []
assert Mapanaliz.all_path_list(%{a1: 1}, []) == [[:a1]]
assert Mapanaliz.all_path_list(%{a1: 1, a2: 1}, []) == [[:a1], [:a2]]
assert Mapanaliz.all_path_list(%{a1: %{b1: 1}}, []) == [[:a1], [:a1, :b1]]
assert Mapanaliz.all_path_list(%{a1: 1, a2: %{b1: 1}}) == [[:a1], [:a2], [:a2, :b1]]
assert Mapanaliz.all_path_list(%{a1: 1, a2: %{a2b1: 1, a2b2: 1}}) ==
[[:a1], [:a2], [:a2, :a2b1], [:a2, :a2b2]]
assert Mapanaliz.all_path_list(%{a: %{c: 1, d: 1}, b: %{e: 1}}) ==
[[:a], [:b], [:a, :c], [:a, :d], [:b, :e]]
assert Mapanaliz.all_path_list(%{z1: %{y11: 1, y12: 1}, z2: %{y21: %{x221: 1}}}) ==
[[:z1], [:z2], [:z1, :y11], [:z1, :y12], [:z2, :y21], [:z2, :y21, :x221]]
end
end
SOLUTION I did it like that using Iteraptor module, but how can i do that using only buildin functions
defmodule Mapanaliz do
def all_path_list(xmap) do
case xmap do
%{} -> []
end
xmap
|> Iteraptor.reduce([], fn {k, _}, acc ->
[Enum.join(k, ", ") | acc]
end, yield: :all)
|> :lists.reverse()
end
Rather than a built-in function, this is typically a case for recursion since you want to iterate on a tree:
defmodule Mapanaliz do
def all_path_list(map, path \\ []) do
Enum.flat_map(map, fn {key, value} ->
new_path = path ++ [key]
children = if is_map(value), do: all_path_list(value, new_path), else: []
[new_path | children]
end)
end
end
Explanation:
we "loop" on the map keys and values
if the value is not a map, we just add the current path
if the value is a map, we get all its children paths by making a recursive call to all_path_list
There are probably many ways to implement this using recursion.
Here I went with Enum.flat_map/2 because we want to merge all paths in a single list, not a list of lists.
But you can probably make it more efficient by passing a list accumulator and reversing the list at the end.
I need to learn the right way to do pattern matching on Pair types:
let pairToBeFiltered = Ok ([(1,[]);(2,[3;4]);(5,[6;7;8]);(9,[]);(10,[])])
let filterEmpty (pair: int * int list) =
match pair with
| (x,y) when y <> [] -> (x,y) //This gives error because of incomplete pattern matching!
let filtering = List.map(filterEmpty) pairToBeFiltered
Desired output:
Ok([(2,[3;4]);(5,[6;7;8])])
This should do it:
let pairsToBeFiltered = Ok ([(1,[]);(2,[3;4]);(5,[6;7;8]);(9,[]);(10,[])])
let filterEmpty pairs =
List.where (fun (_, y) -> y <> []) pairs // pattern match on pair occurs here
let filtering : Result<_, string> =
pairsToBeFiltered
|> Result.map filterEmpty
printfn "%A" filtering // Ok [(2, [3; 4]); (5, [6; 7; 8])]
There are a number of issues here:
For clarity, I modified filterEmpty so it processes the entire list, rather than a single pair. This is where we apply the filtering function, List.where, using pattern matching. (In your code, note that List.map with a match expression doesn't filter anything.)
Since your list is wrapped in a Result, you need to unwrap it via Result.map in order to process it. (Since you didn't specify a 'TError type, I assumed string to pacify the compiler.)
Three more versions:
(* using match statement *)
module Version1 =
let pairsToBeFiltered : Result<_, string> =
Ok [(1,[]);(2,[3;4]);(5,[6;7;8]);(9,[]);(10,[])]
let myWhere (pair : int * List<int>) =
match pair with
| _, [] -> false
| _, _ -> true
let myFilter l0 = l0 |> Result.map (List.filter myWhere)
let result = pairsToBeFiltered |> myFilter
(* using lambda functions and List.isEmpty *)
module Version2 =
let pairsToBeFiltered : Result<_, string> =
Ok [(1,[]);(2,[3;4]);(5,[6;7;8]);(9,[]);(10,[])]
let myFilter l0 =
l0
|> Result.map (fun l1 ->
l1 |> List.filter (fun (_, l2) ->
l2 |> List.isEmpty |> not))
let result = pairsToBeFiltered |> myFilter
(* shortening Version2 (point free style - take care, can be confusing) *)
module Version3 =
let pairsToBeFiltered : Result<_, string> =
Ok [(1,[]);(2,[3;4]);(5,[6;7;8]);(9,[]);(10,[])]
let myFilter = Result.map (List.filter (snd >> List.isEmpty >> not))
let result = pairsToBeFiltered |> myFilter
I've come up with this simple algorithm (convert list of tuples to a map collection of keys to lists) that I needed in my F# code:
let MergeIntoMap<'K,'V when 'K: comparison>(from: seq<'K*'V>): Map<'K,seq<'V>>=
let keys = from.Select(fun (k,v) -> k)
let keyValuePairs = seq {
for key in keys do
let valsForKey = from.Where(fun (k,v) -> key = k).Select(fun (k,v) -> v) |> seq
yield key,valsForKey
}
keyValuePairs |> Map.ofSeq
Example input:
[ ("a", 1); ("b", 2), ("a", 3) ]
Output:
dict [ ("a", [1; 3]), ("b", [2]) ]
And I was thinking this must be something that is already in the BCL or F#'s set of high order functions maybe? If yes, can someone reference me to it? Because I'm sure my code is not very efficient as it is...
It seems you want to get something like that
let toGroupMap x =
x
|> Seq.groupBy fst
|> Seq.map
(fun (k,v) -> k, v |> Seq.map snd |> Seq.toArray)
|> Map.ofSeq
fsi:
val toGroupMap : x:seq<'a * 'b> -> Map<'a,'b []> when 'a : comparison
val input : (string * int) list = [("a", 1); ("b", 2); ("a", 3)]
val output : Map<string,int []> = map [("a", [|1; 3|]); ("b", [|2|])]
Edit
As written Fyodor Soikin in the comments, there is a extension method ToLookup, which probably does what you need.
open System.Linq
let output = input.ToLookup(fst, snd)
You can read here about the difference between ILookup and IDictionary interfaces
It seems like the RTypeProvider can only handle namedParams of the same type. Is this the case?
For example,
open RDotNet
open RProvider
type foo = {
Which: string
Qty: float option
}
let someFoos = [{Which = "that"; Qty = Some 4.0}; {Which = "other"; Qty = Some 2.0}]
let thingForR =
namedParams [
"which", someFoos |> List.map (fun x -> x.Which);
"qty", someFoos |> List.map (fun x -> x.Qty);
]
|> R.data_frame
doesn't work as I get an error on the x.Qty saying
This expression was expected to have type
string
but here has type
float option
If I reverse the order in the thingForR let, then I get the opposite error:
let thingForR =
namedParams [
"qty", someFoos |> List.map (fun x -> x.Qty);
"which", someFoos |> List.map (fun x -> x.Which);
]
|> R.data_frame
Here, the error on x.Which is
This expression was expected to have type
float option
but here has type
string
Can the dictionary in the namedParams not have different types? If so, how can you create a data frame with different types in F# and pass them to R?
You need to box the values inside the dictionary. That way they are all just object. So:
let thingForR =
namedParams [
"which", box (someFoos |> List.map (fun x -> x.Which) );
"qty", box (someFoos |> List.map (fun x -> x.Qty) |> List.map (Option.toNullable >> float));
]
|> R.data_frame
gives me:
val thingForR :
SymbolicExpression = which qty
1 that 4
2 other 2
Please refer to your previous question on float option to convert the Option list to float list. Also string option if necessary.
You can go through Deedle (if not for the option values):
let someFoos' = [{Which = "that"; Qty = 4.0}; {Which = "other"; Qty = 2.0}]
let df' = someFoos' |> Frame.ofRecords
df' |> R.data_frame
I have this datatype:
datatype 'a stream' = Susp of unit -> 'a stream
and 'a stream = Empty | Cons of 'a * 'a stream'
and I want to write a flatten function which has the type below.
flatten: ’a stream’ stream’ -> ’a stream’
The flatten function will take stream of streams as input and flatten it by appending them.
How do I do this? Any ideas?
Thanks.
Edit: I know how to do it for lists. It is quite simple:
fun flatten [] = [] | flat (l::ls) = l # flatten ls;
Help me with streams please, I don't know how to pattern match a stream of a stream.
Let's write it for list first:
fun append(xs, ys) = case xs of
[] => ys
| (x::xs) => x :: append(xs, ys)
fun flatten(xss) = case xss of
[] => []
| (xs::xss) => append(xs, flatten(xss))
The above should be obvious. Now we only need to change it slightly to support stream, by Suspending and force-ing at the appropriate steps:
fun force(Susp(xs)) = xs()
fun append(xs, ys) = case force xs of
Empty => ys
| Cons(x,xs) => Susp(fn () => Cons(x, append(xs, ys)))
fun flatten(xss) = case force xss of
Empty => Susp(fn () => Empty)
| Cons(xs,xss) => append(xs, flatten(xss))