Retrieving the Keys by comparing the values in a map - erlang

I am trying to print the keys by comparing the values of a map with the given input as string (input is given in the below tried executed sample output).
See the below code I tried code:
-module(main).
-compile(export_all).
get_code_for_words(Sentence) ->
Words = string:split(Sentence, "\s", all),
LettersWords = # { "a" => ".- ","b" =>"-... ","c" =>"-.-. ","d" => "-.. ","e" => ". ","f" => "..-. ","g" => "--. ","h" =>".... ","i" =>".. ","j" =>".--- ","k" =>"-.- ","l" =>".-.. ","m" =>"-- ","n" =>"-. ","o" => "--- ","p" =>".--. ","q" =>"--.- ","r" =>".-. ","s" =>"... ","t" =>"- ","u" =>"..- ","v" =>"...- ","w" =>".-- ","x" =>"-..- ","y" =>"-.--","z" =>"--.. "},
Result = get_codes(Words, LettersWords, _AllCodes=[]),
io:format("~p~n", [Result]).
get_codes([Word|Words], Map, AllCodes) ->
NewAllCodes = maps:fold(fun(K,V,Acc) ->
%io:format("~p~n",[Acc]),
case V =:= Word of
true -> [K|Acc];
_ -> Acc
end
end,
AllCodes,
Map),
get_codes(Words, Map, NewAllCodes);
get_codes([], _Map, AllCodes) ->
lists:reverse(AllCodes).
When I executed the code , the output is below:
**
9> c(main).
main.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,main}
10> main:get_code_for_words(".... ..").
[]
ok
**
Please suggest me ?

Let us first reformat your code (with the help of rebar3_format):
get_code_for_words(Sentence) ->
Words = string:split(Sentence, "\s", all),
LettersWords =
#{"a" => ".- ",
"b" => "-... ",
"c" => "-.-. ",
"d" => "-.. ",
"e" => ". ",
"f" => "..-. ",
"g" => "--. ",
"h" => ".... ",
"i" => ".. ",
"j" => ".--- ",
"k" => "-.- ",
"l" => ".-.. ",
"m" => "-- ",
"n" => "-. ",
"o" => "--- ",
"p" => ".--. ",
"q" => "--.- ",
"r" => ".-. ",
"s" => "... ",
"t" => "- ",
"u" => "..- ",
"v" => "...- ",
"w" => ".-- ",
"x" => "-..- ",
"y" => "-.--",
"z" => "--.. "},
Result = get_codes(Words, LettersWords, _AllCodes = []),
io:format("~p~n", [Result]).
get_codes([Word | Words], Map, AllCodes) ->
NewAllCodes =
maps:fold(fun(K, V, Acc) ->
case V =:= Word of
true -> [K | Acc];
_ -> Acc
end
end,
AllCodes,
Map),
get_codes(Words, Map, NewAllCodes);
get_codes([], _Map, AllCodes) ->
lists:reverse(AllCodes).
Now, let's isolate the map so I don't have to copy it around all the time and, since this doesn't look like an escript, let's just remove the call to io:format/2 for the result…
letters_to_words() ->
#{"a" => ".- ",
…
"z" => "--.. "}.
get_code_for_words(Sentence) ->
Words = string:split(Sentence, "\s", all),
get_codes(Words, letters_to_words(), _AllCodes = []).
Now, let's talk about get_codes/3…
Since that function is using the map above, I'll assume that you when you say Word, you actually mean Character (since otherwise you'll have to go char-by-char, but this function goes word-by-word)…
Let's first use pattern-matching instead of a boolean case statement…
get_codes([Word | Words], Map, AllCodes) ->
NewAllCodes =
maps:fold(fun(K, V, Acc) ->
case V of
Word -> [K | Acc];
_ -> Acc
end
end,
AllCodes,
Map),
get_codes(Words, Map, NewAllCodes);
get_codes([], _Map, AllCodes) ->
lists:reverse(AllCodes).
Now, I'll assume that you expect this to happen…
8> a_module:get_code_for_words(".- -.-.").
["a","c"]
But, instead, you get…
8> a_module:get_code_for_words(".- -.-.").
[]
And, to understand why that happens, let's see what's the result of string:split/3 in your case…
9> string:split(".- -.-.", "\s", all).
[".-","-.-."]
As you can see, those strings have no trailing spaces, like the keys on your map. So… replacing your map with the following one should fix your issue…
letters_to_words() ->
#{"a" => ".-",
"b" => "-...",
"c" => "-.-.",
"d" => "-..",
"e" => ".",
"f" => "..-.",
"g" => "--.",
"h" => "....",
"i" => "..",
"j" => ".---",
"k" => "-.-",
"l" => ".-..",
"m" => "--",
"n" => "-.",
"o" => "---",
"p" => ".--.",
"q" => "--.-",
"r" => ".-.",
"s" => "...",
"t" => "-",
"u" => "..-",
"v" => "...-",
"w" => ".--",
"x" => "-..-",
"y" => "-.--",
"z" => "--.."}.

Related

Trying to compare the each element of a list with the keys of a Map [I tried with the following code]

I tried the code below , whereas the M1 is the Map consists of M1= #{a =>"Apple",b =>"Ball"}, and Str is the given by user ex: fun("ab").
I want to print the relevant value of the key in Map M1 based on the given string Str.
Tried Code:
fun(Str) ->
X = [ [X] || X <- Str],
Key = maps:keys(M1),
mOrse_convert(X,Key)
end;
mOrse_convert([],Key) ->
true;
mOrse_convert([First|Rest],Key) ->
case Key of
#{ X := A} -> A
end
mOrse_convert(Rest,Key)
end.
Can anyone help/suggest me ?
This function has 2 arguments: Str - value to find and target Map. The function returns list of all keys in Map that have value equals Str.
-module(ex).
-export([func/2]).
func(Str, Map) ->
Fun = fun(K, V, Acc) ->
if V == Str -> [K | Acc];
true -> Acc
end
end,
maps:fold(Fun, [], Map).
Go to Erlang shell and type following command:
1> c("ex.erl").
{ok,ex}
2> ex:func("a", #{1 => "a", 2 => "b", 3 => "ab"}).
[1]
3> ex:func("a", #{1 => "a", 2 => "b", 3 => "ab", 4 => "a"}).
[4,1]
Thank you Alexei , the solution provided by you is working good for an alphabet and got the output only for single character and given below, but I want to pass the List as input in as Str , I tried the below code as List as input,
Output for single alphabet :
20> c(morse).
{ok,morse}
21> morse:morse_convert("s").
Single ok
My Required output is :
20> c(morse).
{ok,morse}
21> morse:morse_convert("abc").
Apple Ball Cat ok
Tried Code :
X = [ [X] || X <- Str],
Y = func(X,Morse),
io:format(Y ).
func([],{}) -> io:fwrite("\nCompleted.\n");
func([First | Last], Morse) ->
Fun = fun(K, V, Acc) ->
if K == First -> [V | Acc];
true -> Acc
end
end,
maps:fold(Fun, [], Morse),
func(Last,Morse).
-module(a).
-compile(export_all).
cypher() ->
#{"a" =>"Apple",
"b" => "Ball",
"c" => "Cat"}.
convert([]) -> io:format("~n");
convert([Int|Ints]) ->
Word = maps:get([Int], cypher()),
io:format("~s ", [Word]),
convert(Ints).
In the shell:
~/erlang_programs$ erl
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V9.3 (abort with ^G)
1> c(a).
a.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,a}
2> a:convert("abc").
Apple Ball Cat
ok
3>
An erlang string, e.g. "abc", is actually a shortcut for creating a list of integers, where each integer is the ascii code of a letter in the string. For example:
3> "abc" =:= [97,98,99].
true
When the shell outputs a string, the shell expects you to know that the string is really a list of integers, which is confusing. To eliminate that confusion, you can tell the shell not to output strings by executing the function shell:strings(false):
5> "abc".
"abc"
6> shell:strings(false).
true
7> "abc".
[97,98,99]
Then you can see what you are really dealing with.
Next, when you use pattern matching to remove an integer from a list, you have an integer--not a string. To create a string from an integer, you need to put the integer into a list:
[Int]
You can see that here:
2> 97 =:= "a".
false
3> [97] =:= "a".
true
It's not clear whether you have atoms or strings as the keys in your map or whether you don't care. If your map must have atoms as the keys, you can convert strings to atoms using list_to_atom/1:
8> list_to_atom([97]).
a
Finally, when you want to output a string, e.g. one of the values in the map, rather than a list (after executing shell:strings(false)), you can use the ~s formatting sequence:
16> io:format("~s~n", ["Apple"]).
Apple
ok
Using a list comprehension would look like this:
convert(Str) ->
Cypher = #{"a" =>"Apple",
"b" => "Ball",
"c" => "Cat"
},
[
io:format("~s ", [maps:get([Int], Cypher)] )
|| Int <- Str
],
io:format("~n").
A list comprehension always returns a list, in this case [ok, ok, ok], which is not something you care about--you only care about the side affect of outputting the values. So, you can ignore the list and return something else, i.e. whatever io:format/1 returns, which is ok.
Remember, it's less confusing to have the shell output lists rather than strings, then only output strings when you specifically tell the shell to do it.
I was not sure to understand what you are looking for, here are 2 interpretations.
1> M = #{a =>"Apple",b =>"Ball",c=>"Corn",d=>"Donut",e=>"Ball"}.
#{a => "Apple",b => "Ball",c => "Corn",d => "Donut",
e => "Ball"}
2> L = ["Ball","Donut"].
["Ball","Donut"]
3> % first, get list of keys from list of values: expected result = [b,d,e]
3> [K || {K,V} <- maps:to_list(M), lists:member(V,L) == true].
[b,d,e]
4> L1 = "ace".
"ace"
5> % second, get list of values from list of keys: expected result ["Apple","Corn","Ball"]
5> [maps:get(list_to_atom([X]),M) || X <- L1].
["Apple","Corn","Ball"]
6>

Pattern matching map with exact list of keys

In order to validate some JSON documents, I need to ensure that my JSON (represented has a map) has a specific set of fields not more, not less.
From the best of my knowledge pattern matching with something like #{a := FieldA} = MyJSON only ensures that the key a is present in MyJSON.
Unfortunately maps:with/2 ignores missing keys and most other maps functions work on a single key at the time.
In this case, what is the recommended way to ensure that a map contains a specific field or list of fields and not any other?
My current, ugly, solution is to pattern match all the keys I need and rebuild a new map like in:
validate(#{a := A, b := B, c := C}) ->
#{a => A, b => B, c => C}.
But it becomes very hard to maintain, very quickly.
if i understand your problem well, this code should do what you want:
is_map_valid(Map,SortedKeyList) ->
SortedKeyList == lists:sort(maps:keys(Map)).
tested in the shell:
1> Is_map_valid = fun(Map,SortedKeyList) -> SortedKeyList == lists:sort(maps:keys(Map)) end.
#Fun<erl_eval.12.99386804>
2> Map1 = #{a => 1, b => 2}.
#{a => 1,b => 2}
3> Map2 = #{a => 1, b => 2, c => 3}.
#{a => 1,b => 2,c => 3}
4> Map3 = #{a => 1, b => 2, c => 3, d => 4}.
#{a => 1,b => 2,c => 3,d => 4}
5> Keys = [a,b,c].
[a,b,c]
6> Is_map_valid(Map1,Keys).
false
7> Is_map_valid(Map2,Keys).
true
8> Is_map_valid(Map3,Keys).
false
9>
If I understand correctly: use maps:size (or erlang:map_size if you need it in a guard condition) after maps:with, i.e. something like
validate(Keys, Map) ->
ExpectedSize = erlang:length(Keys),
case maps:with(Keys, Map) of
Map1 when erlang:map_size(Map1) == ExpectedSize -> {ok, Map1};
_ -> invalid
end.
You can also check Map size first to fail quickly.

Replace key in nested maps

How can i replace all keys in the nested map with another key without knowing the path (or replace keys, that match a pattern), for example:
Map = #{foo => #{bar => 1, foo => #{bar => 2}}}.
Map2 = maps:keyreplace(bar, foo, Map).
>>> #{foo => #{foo => 1, foo => #{foo => 2}}}
What is the most efficient way to do this?
Replacing key in a flat map seems to be pretty simple:
keyreplace(K1, K2, M) when is_map(M) ->
case maps:take(K1, M) of
{V, M2} ->
maps:put(K2, V, M2);
error -> M
end.
Or like that, maybe:
keymap(F, Map) ->
maps:fold(fun(K, V, Acc) ->
maps:put(F(K), V, Acc)
end, #{}, Map).
Map = #{foo => 1, bar => 2}.
Fun = fun(K) -> atom_to_list(K) end.
Map2 = keymap(Fun, Map).
>>> #{"bar" => 2,"foo" => 1}
I wrote recursive function which you can use to achieve your goal, it takes 3 arguments:
Is_matched - function in which you check that key match your pattern.
Convert - function which converts key as you want.
Map - map to process.
replace_keys(Is_matched, Convert, Map) when is_map(Map) ->
maps:fold(fun (K, V, AccIn) ->
NewKey =
case Is_matched(K) of
true -> Convert(K);
false -> K
end,
maps:put(NewKey, replace_keys(Is_matched, Convert, V), AccIn)
end, #{}, Map);
replace_keys(_, _, Term) -> Term.
Example:
1> Map = #{a => "1", <<"b">> => #{c => "2", <<"d">> => 3}}.
#{a => "1",<<"b">> => #{c => "2",<<"d">> => 3}}
2> Is_matched = fun(Term) -> is_atom(Term) end.
#Fun<erl_eval.6.99386804>
3> Convert = fun(Term) -> atom_to_list(Term) end.
#Fun<erl_eval.6.99386804>
4> keyreplace:replace_keys(Is_matched, Convert, Map).
#{"a" => "1",<<"b">> => #{"c" => "2",<<"d">> => 3}}
This can be done with some modifications to your maps:fold attempt:
If the third argument is not a map, return it as is.
When folding:
Recursively call the function with the value field to obtain the new value.
If the key matches K1, put the new value into K2.
Your example output is incorrect - it has 2 values for the foo key. I've modified it in the test below.
keyreplace(K1, K2, Map) when is_map(Map) ->
maps:fold(fun(K, V, Acc) ->
Key = if K == K1 -> K2; true -> K end,
Value = keyreplace(K1, K2, V),
maps:put(Key, Value, Acc)
end, #{}, Map);
keyreplace(_K1, _K2, Term) -> Term.
1> c(a).
{ok,a}
2> Map = #{foo => #{bar => 1, baz => #{bar => 2}}}.
#{foo => #{bar => 1,baz => #{bar => 2}}}
3> a:keyreplace(bar, foo, Map).
#{foo => #{baz => #{foo => 2},foo => 1}}

Erlang - merging maps

I am trying to figure out how to merge two maps in a way that allows me to process elements with the same keys.
For example, merging
#{"Ala" => 1,"kota" => 3}
with
#{"kota" => 4}
should result in:
#{"Ala" => 1,"kota" => 7}
There's no builtin function in Erlang that does exactly this but it can be done with maps:fold/3 and maps:update_with/4 like this:
1> A = #{"Ala" => 1,"kota" => 3}.
#{"Ala" => 1,"kota" => 3}
2> B = #{"kota" => 4}.
#{"kota" => 4}
3> maps:fold(fun(K, V, Map) -> maps:update_with(K, fun(X) -> X + V end, V, Map) end, A, B).
#{"Ala" => 1,"kota" => 7}
The code basically does this: for each item in B, if the same key exists in A, it gets the value (V) and adds the current value(X). If it doesn't exist, it sets the value to V
This function will merge two maps, running a function (Fun) where the key exists in both maps. It also handled the situation where Map1 is bigger than Map2 and vice-versa.
map_merge(Map1, Map2, Fun) ->
Size1 = maps:size(Map1),
Size2 = maps:size(Map2),
if
Size1 > Size2 ->
Folder = fun(K, V1, Map) ->
maps:update_with(K, fun(V2) -> Fun(K, V1, V2) end, V1, Map)
end,
maps:fold(Folder, Map1, Map2);
true ->
Folder = fun(K, V1, Map) ->
maps:update_with(K, fun(V2) -> Fun(K, V2, V1) end, V1, Map)
end,
maps:fold(Folder, Map2, Map1)
end.
Example usage in the erl shell:
1> my_module:map_merge(#{"a" => 10, "b" => 2}, #{"a" => 1}, fun(K, V1, V2) -> V1 + V2 end).
#{"a" => 11,"b" => 2}
2> my_module:map_merge(#{"a" => 10}, #{"a" => 1, "b" => 2}, fun(K, V1, V2) -> V1 + V2 end).
#{"a" => 11,"b" => 2}
3> my_module:map_merge(#{"a" => 10},#{"a" => 10}, fun(K, V1, V2) -> V1 + V2 end).
#{"a" => 20}

Ruby: stringA.gsub(/\s+/, '') versus stringA.strip

Say
string = "Johnny be good! And smile :-) "
Is there a difference between
string.gsub(/\s+/, '')
and
string.strip
?
If so, what is it?
strip only removes leading and trailing whitespace, using gsub in the way that you outline in your question will remove all whitespace from the string.
irb(main):004:0* " hello ".strip
=> "hello"
irb(main):005:0> " h e l l o ".strip
=> "h e l l o"
irb(main):006:0> " hello ".gsub(/\s+/, '')
=> "hello"
irb(main):007:0> " h e l l o ".gsub(/\s+/, '')
=> "hello"

Resources