Erlang Thursday – dict:merge/3

Today’s Erlang Thursday is on dict:merge/3.

dict:merge/3 takes 3 arguments, the first argument is a merge function to be called when there is a key collision, and the second and third arguments are dictionaries.

The merge function is a function that takes the key as the first argument, the value from the first dictionary as the second argument, and the value from the second dictionary as the the third argument.

dict:merge(fun (_Key, Value1, Value2) -> [Value1, Value2] end,
           dict:from_list([{a, 1}, {b, 2}, {x, 5}]),
           dict:from_list([{x, 7}, {y, 8}, {z, 10}])).
% {dict,5,16,16,8,80,48,
%       {[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},
%       {{[],
%         [[a|1]],
%         [[b|2]],
%         [],[],[],[],[],
%         [[x,5,7]],
%         [[y|8]],
%         [[z|10]],
%         [],[],[],[],[]}}}

dict:merge(fun (_Key, Value1, Value2) -> Value1 * Value2 end,
           dict:from_list([{a, 1}, {b, 2}, {x, 5}]),
           dict:from_list([{x, 7}, {y, 8}, {z, 10}])).
% {dict,5,16,16,8,80,48,
%       {[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},
%       {{[],
%         [[a|1]],
%         [[b|2]],
%         [],[],[],[],[],
%         [[x|35]],
%         [[y|8]],
%         [[z|10]],
%         [],[],[],[],[]}}}

The merge function passed to dict:merge/3 only gets called in the case of a collision, as shown below. Note that there is a call to exit in the body of the function which would cause the process to terminate if the function was ever invoked.

dict:merge(fun (_Key, _Value1, _Value2) -> exit(merge_happened) end,
           dict:from_list([{a, 1}, {b, 2}]),
           dict:from_list([{x, 7}, {y, 8}, {z, 10}])).
% {dict,5,16,16,8,80,48,
%       {[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},
%       {{[],
%         [[a|1]],
%         [[b|2]],
%         [],[],[],[],[],
%         [[x|7]],
%         [[y|8]],
%         [[z|10]],
%         [],[],[],[],[]}}}

If you wish to treat the merge as an overlay of the second dictionary over the first, the merge function just needs to return the value from the second dictionary in the case of a key conflict.

dict:merge(fun (_Key, _Value1, Value2) -> Value2 end,
           dict:from_list([{a, 1}, {b, 2}, {x, 5}]),
           dict:from_list([{x, 7}, {y, 8}, {z, 10}])).
% {dict,5,16,16,8,80,48,
%       {[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},
%       {{[],
%         [[a|1]],
%         [[b|2]],
%         [],[],[],[],[],
%         [[x|7]],
%         [[y|8]],
%         [[z|10]],
%         [],[],[],[],[]}}}

If you want to keep all of the keys and values in the first dictionary, and just add the keys and values that are in the second dictionary, but not in the first dictionary, the merge function should just return the value associated with the first dictionary.

dict:merge(fun (_Key, Value1, _Value2) -> Value1 end,
           dict:from_list([{a, 1}, {b, 2}, {x, 5}]),
           dict:from_list([{x, 7}, {y, 8}, {z, 10}])).
% {dict,5,16,16,8,80,48,
%       {[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},
%       {{[],
%         [[a|1]],
%         [[b|2]],
%         [],[],[],[],[],
%         [[x|5]],
%         [[y|8]],
%         [[z|10]],
%         [],[],[],[],[]}}}

Just a peek into the new Maps that came in to Erlang in the 17.0 release.

–Proctor

,

Leave a Comment

Ruby Tuesday – Hash#merge

Today’s Ruby Tuesday covers Hash#merge.

Hash#merge takes a hash as it’s argument, and returns a new hash with the combined items from both hashes.

{a: 1, b: 2, c: 3}.merge({d: 1, e: 2, f: 3})
# {:a=>1, :b=>2, :c=>3, :d=>1, :e=>2, :f=>3}

Hash#merge has the ability to take a block which allows you to specify behavior of what you would like to happen when key collisions are encountered.

{a: 1, b: 2, c: 3, d: 4}.merge({a: 4, s: 3, d: 2, f: 1}) {|key, val1, val2| val1 * val2 }
# {:a=>4, :b=>2, :c=>3, :d=>8, :s=>3, :f=>1}
{a: 1, b: 2, c: 3, d: 4}.merge({a: 4, s: 3, d: 2, f: 1}) {|key, val1, val2| [val1, val2] }
# {:a=>[1, 4], :b=>2, :c=>3, :d=>[4, 2], :s=>3, :f=>1}

If a key is present in both hashes and no block is given, Hash#merge takes the value for the key from the hash that is passed into the merge method. This behaves as if you passed a block that returned the value for the second hash.

{a: 1, b: 2, c: 3, d: 4}.merge({a: 4, s: 3, d: 2, f: 1})
# {:a=>4, :b=>2, :c=>3, :d=>2, :s=>3, :f=>1}
{a: 1, b: 2, c: 3, d: 4}.merge({a: 4, s: 3, d: 2, f: 1}) {|key, val1, val2| val2 }
# {:a=>4, :b=>2, :c=>3, :d=>2, :s=>3, :f=>1}

If you would rather have the value from the hash that merge is being called on in the case of a key collision, you can provide a block that returns the value from the first hash.

{a: 1, b: 2, c: 3, d: 4}.merge({a: 4, s: 3, d: 2, f: 1})
# {:a=>4, :b=>2, :c=>3, :d=>2, :s=>3, :f=>1}
{a: 1, b: 2, c: 3, d: 4}.merge({a: 4, s: 3, d: 2, f: 1}) {|key, val1, val2| val1 }
# {:a=>1, :b=>2, :c=>3, :d=>4, :s=>3, :f=>1}

Hash#merge is especially useful if you take in an options hash of values because you don’t want to force the consumer of the method to have to specify every single property that someone could possibly want.

By using Hash#merge, your method can have a hash that represents the defaults, which you merge with the options that they passed in to get a complete list of all of the possible options.

defaults = {happy: true, hungry: false, sleepy: false}
# {:happy=>true, :hungry=>false, :sleepy=>false}
method_args = {hungry: true}
# {:hungry=>true}
settings = defaults.merge(method_args)
# {:happy=>true, :hungry=>true, :sleepy=>false}

–Proctor

,

Leave a Comment

Erlang Thursday – string:join/2

Today’s Erlang Thursday is on string:join/2.

string:join/2 takes a list of strings as its first argument, and a string separator used to join the strings together into a single string.

91> string:join(["a", "b", "c"], "").
"abc"
92> string:join(["a", "b", "c"], "-").
"a-b-c"

The separator string can be a string of any length, and doesn’t just have to be a single character.

93> string:join(["a", "b", "c"], "___").
"a___b___c"
94> string:join(["a", "b", "c"], " ").  
"a b c"

And as with any string, a list of characters, or even integers, can be used as the separator string.

string:join(["a", "b", "c"], [$A]).
# "aAbAc"
string:join(["a", "b", "c"], [52]).
# "a4b4c"

–Proctor

,

Leave a Comment

Ruby Tuesday – Array#join

Today’s Ruby Tuesday is on Array#join

["Now", "is", "the", "time", "for", "all", "good", "people"].join(" ")
# "Now is the time for all good people"
["1", "2", "3", "4", "5"].join("x")
# "1x2x3x4x5"

If the array contains items that are not strings, they will be coerced to strings and then joined together.

[1, 2, 3, 4, 5].join(" ")
"1 2 3 4 5"
[1, 2, 3, 4, 5].join("x")
# "1x2x3x4x5"
[1, 2, 3, 4, 5].join
# "12345"

If no argument is passed to Array#join, it uses the value set in $,.

$, = "\n"
# "\n"
[1, 2, 3, 4, 5].join
# "1\n2\n3\n4\n5"

–Proctor

,

Leave a Comment

Erlang Thursday – string:tokens/2

Today’s Erlang Thursday is string:tokens/2.

string:tokens/2 takes a string as the first argument, and a list of separators to split the string on, and returns a list of token strings.

string:tokens("foo", "").
% ["foo"]
string:tokens("banana", "a").
% ["b","n","n"]
string:tokens("It was the best of times, it was the worst of times", " ").
% ["It","was","the","best","of","times,","it","was","the",
%  "worst","of","times"]

If consecutive separators appear in the string they will be treated as a single separator, and no empty strings will be returned.

string:tokens("Mississippi", "s").
% ["Mi","i","ippi"]
65> string:tokens("Mississippi", "sp").
% ["Mi","i","i","i"]
string:tokens("Mississippi", "is").
% ["M","pp"]

The order of the separators in the separator list passed to string:tokens/2 does not matter, and can be specified in any order.

string:tokens("Mississippi", "ps").
% ["Mi","i","i","i"]
65> string:tokens("Mississippi", "sp").
% ["Mi","i","i","i"]

And as the separator list is just simply a list of separators, instead of passing a string, the integer values for the characters to use as the separators can be passed as a list, as a list of the integers is the same as a string.

$s.
% 115
$p.
% 112
[115, 112].
% "sp"
string:tokens("Mississippi", [115]).
% ["Mi","i","ippi"]
string:tokens("Mississippi", [115, 112]).
% ["Mi","i","i","i"]

–Proctor

,

Leave a Comment

Ruby Tuesday – String#split

Today’s Ruby Tuesday is on String#split.

String#split takes a pattern as it’s parameter, and uses that pattern as the deliminator[1] to split the string into an Array of tokens.

"It was the best of times, it was the worst of times".split
# => ["It", "was", "the", "best", "of", "times,", "it", "was",
#     "the", "worst", "of", "times"]
"It was the best of times, it was the worst of times".split(",")
# => ["It was the best of times", " it was the worst of times"]

If String#split is called with a single space as the pattern, it removes any leading and trailing whitespace, but if another pattern is specified, it will return empty strings representing a sequence of the deliminators.

"continuous        whitespace".split
# => ["continuous", "whitespace"]
" \t  continuous \t     whitespace \t".split(" ")
# => ["continuous", "whitespace"]
"fizzle".split("z")
# => ["fi", "", "le"]
"continuous        whitespace".split(/\s/)
# => ["continuous", "", "", "", "", "", "", "", "whitespace"]

String#split can take a regular expression as the pattern in addition to taking a string.

"fizzle".split(/z/)
# => ["fi", "", "le"]
"fizZle".split(/[zZ]/)
=> ["fi", "", "le"]

If an empty pattern is passed to String#split then the string is split into a list of it’s characters.

"fizzle".split(//)
# => ["f", "i", "z", "z", "l", "e"]
"banana".split("")
=> ["b", "a", "n", "a", "n", "a"]

String#split can take a second argument, which is a limit to the number of items returned in the array. A value of zero, behaves as if the limit was not passed as an argument. If the value for limit is positive it will return at most that many elements total, and will include any trailing empty strings, or “null fields” as called by the Ruby documentation, up to the limit. If a negative integer is passed as a value, all of the trailing empty strings that tokenized will be returned.

"banana".split("a")
# => ["b", "n", "n"]
"banana".split("a", 0)
# => ["b", "n", "n"]
"banana".split("a", 1)
# => ["banana"]
"banana".split("a", 2)
# => ["b", "nana"]
"banana".split("a", 4)
# => ["b", "n", "n", ""]
"banana".split("a", 7)
# => ["b", "n", "n", ""]
"bananaaa".split("a", 7)
# => ["b", "n", "n", "", "", ""]
"banana".split("a", -1)
# => ["b", "n", "n", ""]
"banana".split("a", -2)
# => ["b", "n", "n", ""]
"banana".split("a", -4)
# => ["b", "n", "n", ""]
"bananaaaaaaa".split("a", -1)
# => ["b", "n", "n", "", "", "", "", "", "", ""]

–Proctor


[1] Deliminator – A portmanteau of delimiter and eliminate, signifying that the delimiter is to be removed from the results. back

,

Leave a Comment

Erlang Thursday – lists:dropwhile/2

Today’s Erlang Thursday is lists:dropwhile/2.

lists:dropwhile/2 takes a predicate function and a list, and returns a list where the first series of items for which the predicate function returned true have been removed.

lists:dropwhile(fun erlang:is_atom/1, [hello, 'World', foo, 1, 3, 4]).
% [1,3,4]
lists:dropwhile(fun (X) -> X > 0 end, [-1, 0, 1, 2, 3]).
% [-1,0,1,2,3]
lists:dropwhile(fun (X) -> X > 0 end, [-2, -1, 0, 1, 2, 3]).
% [-2,-1,0,1,2,3]
lists:dropwhile(fun (X) -> X < 0 end, [-2, -1, 0, 1, 2, 3]).
% [0,1,2,3]
lists:dropwhile(fun (X) -> X < 0 end, [0, -1, -2, -3, -4, -5]). 
% [0,-1,-2,-3,-4,-5]
lists:dropwhile(fun (X) -> true end, [hello, 'World', foo, 1, 3, bar, 4]). 
% []
lists:dropwhile(fun (X) -> false end, [hello, 'World', foo, 1, 3, bar, 4]).
% [hello,'World',foo,1,3,bar,4]

Unlike lists:filter/2, lists:dropwhile/2 stops checking the list as soon as the predicate function returns false. This means that elements for which the predicate function would return true can still appear in the result list, as if they occur after an element for which the predicate function returns false.

lists:dropwhile(fun erlang:is_atom/1, [hello, 'World', foo, 1, 3, bar, 4]).
% [1,3,bar,4]
lists:filter(fun (X) -> not is_atom(X) end, [hello, 'World', foo, 1, 3, bar, 4]).     
% [1,3,4]
lists:dropwhile(fun (X) -> X < 0 end, [-2, -1, 0, 1, -5, 3, 7]).
% [0,1,-5,3,7]
lists:filter(fun (X) -> X >= 0 end, [-2, -1, 0, 1, -5, 3, 7]).   
% [0,1,3,7]

–Proctor

,

Leave a Comment

Ruby Tuesday – Enumerable#drop_while

Today’s Ruby Tuesday entry is on Enumerable#drop_while.

Enumerable#drop_while drops items from the beginning of the enum up to, but not including, the first element for which the block returns a non-truthy value.

[1, 2, 3, 4, 5, 6].drop_while {|item| item < 5}
# => [5, 6]
["a", "foo", "snafu"].drop_while {|item| item.length < 3}
=> ["foo", "snafu"]
[1, 4, 2, 5, 3, 6].drop_while {|item| true}
# => []
[1, 4, 2, 5, 3, 6].drop_while {|item| false}
# => [1, 4, 2, 5, 3, 6]
[].drop_while{|item| true }
# => []
[].drop_while{|item| false }
# => []

Evaluation stops, and does not check the rest of the list if a falsey value is returned. This will leave other values in the list that might return a falsey value, unlike Enumerable#reject which removes all items from the enum.

[1, 4, 2, 5, 3, 6].drop_while {|item| item < 5}
# => [5, 3, 6]

Enumerable#drop_while can also leave values in your enum that would normally cause the block to error out, because the evaluation against the list stops after the first falsey value is encountered.

["a", "foo", "snafu", "b", :c].drop_while {|item| item.length < 3}
# => ["foo", "snafu", "b", :c]

–Proctor

,

Leave a Comment

Erlang Thursday – lists:filter/2

Today’s Erlang Thursday is on lists:filter/2.

lists:filter/2 takes two arguments, a predicate function and a list to iterate over. The return value is a list of items for which the predicate function returns true for that item.

lists:filter(fun (X) -> X rem 2 =:= 1 end, [1, 2, 3, 4, 5]).
% [1,3,5]
lists:filter(fun erlang:is_atom/1, [1, a, 3, {a, b}, 'World', foo]).
% [a,'World',foo]
lists:filter(fun (X) -> X > 0 end, [1, 0, -3, foo, -13, 43]).
% [1,foo,43]
lists:filter(fun (X) -> X > 0 end, []).                      
% []
lists:filter(fun (X) -> false end, [1, 2, 3, 4, 5]).
% []
lists:filter(fun (X) -> true end, [1, 2, 3, 4, 5]). 
% [1,2,3,4,5]

–Proctor

,

1 Comment

git checkout -

For those familiar with the bash shell, you may know that it has the ability to change directory to the last directory you were in by using the cd - command.

$ cd /var/log/
$ cd /usr/local/bin
$ pwd
/usr/local/bin
$ cd -
/var/log
$ cd -
/usr/local/bin
$

Lo and behold, Git has a similar feature as well, git checkout -. This command will take you back to the last branch/tag/commit you had checked out

$ git status
On branch some_very_long_branch_name
nothing to commit, working directory clean
$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
$ git fetch
$ git pull
Already up-to-date.
$ git checkout -
Switched to branch 'some_very_long_branch_name'
$

–Proctor

Leave a Comment