Erlang code split String without delimiter and put into list - erlang

I am very new to programming in Erlang.
I am making a program to decode braille but I am having a problem trying to split a string every 2 characters with the absence of a delimiter, and putting them into a list.
First I read in the braille alphabet like this:
inKey(Key1) -> Key1.
inKey2(Key2) -> Key2.
inKey3(Key3) -> Key3.
Key 1-3 are strings and look like this:"x.x.xxxxx.xxxxx..x.xx.x.xxxxx.xxxxx..x.xx.x..xxxxxx." these 3 keys form the braille information that I will later use to transform braille into normal characters.
Now I need to split this string and place them in a list so that it would look like this: ["x.","x.","xx","xx",x.","xx","xx","x.",".x and so on.
If the string is split I want to insert them into my coding list like shown in Tuplet for the character A
Code=[#row{name="R1",alfabet=[#codes{letter="A",braille="X."},#codes{letter="B",braille=""}
Can someone helpe me out?
`

In Erlang, you need to remember that a string is equivalent to a list of numbers, where the numbers are the ascii codes for each character. The confusing thing is that sometimes the shell displays a list of numbers as a string, and sometimes it displays a list of numbers as a list of numbers. That is a TERRIBLE feature of the erlang shell.
Despite what the shell displays, just remember that a string is a list of numbers. The problem then becomes, what if you want to output a list of numbers and not a string? The answer is: you can't do anything about that; the shell may display your list of numbers as a string...unless you take further action:
45> Grades = [97,98,99].
"abc"
Wtf??!
46> io:format("~w~n", [Grades]).
[97,98,99]
ok
Another way of thinking about it is this: the erlang string syntax "abc" is just a shortcut for creating the list of numbers [97,98,99].
Next, you can deconstruct a list with a pattern like this:
[Head|Tail] = [1,2,3]
In the shell:
8> [Head|Tail] = [1, 2, 3, 4].
[1,2,3,4]
9> Head.
1
10> Tail.
[2,3,4]
But, the cons operator | is more flexible than that, and it allows you to do this:
13> [N1,N2 | T] = [1, 2, 3, 4].
[1,2,3,4]
14> N1.
1
15> N2.
2
16> T.
[3,4]
Therefore, you can do this:
-module(my).
-compile(export_all).
string() ->
"x.x.xxxxx.xxxxx..x.xx.x.xxxxx.xxxxx..x.xx.x..xxxxxx.".
chunk2([]) -> [];
chunk2([N1, N2| Tail]) ->
[[N1,N2] | chunk2(Tail) ].
In the shell:
2> c(my).
my.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,my}
3> my:chunk2(my:string()).
["x.","x.","xx","xx","x.","xx","xx","x.",".x",".x","x.",
"x.","xx","xx","x.","xx","xx","x.",".x",".x","x.","x.",".x",
"xx","xx","x."]
4>
Finally, to construct a list of #code{} records, you can do this:
-module(my).
-compile(export_all).
-record(codes, {letter, braille}).
string() ->
"x.x.xxxxx.xxxxx..x.xx.x.xxxxx.xxxxx..x.xx.x..xxxxxx.".
chunk2([]) -> [];
chunk2([N1, N2| Tail]) ->
[[N1,N2] | chunk2(Tail) ].
make_record_list(Letters, Chunks) ->
lists:zipwith(
fun(Letter, Chunk) -> #codes{letter=[Letter], braille=Chunk} end,
Letters,
Chunks
).
In the shell:
31> c(my).
my.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,my}
32> ListOfCapLetters = lists:seq($A, $Z).
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
33> BrailleChunks = my:chunk2(my:string()).
["x.","x.","xx","xx","x.","xx","xx","x.",".x",".x","x.",
"x.","xx","xx","x.","xx","xx","x.",".x",".x","x.","x.",".x",
"xx","xx","x."]
34> Records = my:make_record_list(ListOfCapLetters, BrailleChunks).
[{codes,"A","x."},
{codes,"B","x."},
{codes,"C","xx"},
{codes,"D","xx"},
{codes,"E","x."},
{codes,"F","xx"},
{codes,"G","xx"},
{codes,"H","x."},
{codes,"I",".x"},
{codes,"J",".x"},
{codes,"K","x."},
{codes,"L","x."},
{codes,"M","xx"},
{codes,"N","xx"},
{codes,"O","x."},
{codes,"P","xx"},
{codes,"Q","xx"},
{codes,"R","x."},
{codes,"S",".x"},
{codes,"T",".x"},
{codes,"U","x."},
{codes,"V","x."},
{codes,"W",".x"},
{codes,"X","xx"},
{codes,"Y","xx"},
{codes,"Z",[...]}]
It looks like there may be a problem with the last record, so let's check:
37> tl(Records).
[{codes,"B","x."},
{codes,"C","xx"},
{codes,"D","xx"},
{codes,"E","x."},
{codes,"F","xx"},
{codes,"G","xx"},
{codes,"H","x."},
{codes,"I",".x"},
{codes,"J",".x"},
{codes,"K","x."},
{codes,"L","x."},
{codes,"M","xx"},
{codes,"N","xx"},
{codes,"O","x."},
{codes,"P","xx"},
{codes,"Q","xx"},
{codes,"R","x."},
{codes,"S",".x"},
{codes,"T",".x"},
{codes,"U","x."},
{codes,"V","x."},
{codes,"W",".x"},
{codes,"X","xx"},
{codes,"Y","xx"},
{codes,"Z","x."}]
Nope, the first output just reached the limit of what the shell was willing to display.
Note that each element of ListOfCapLetters is a number, and a number is not a list, so each element of ListOfCapLetters is not a string itself. To create a string from a number, you need to put it inside a list, hence [Letter]. It's the same difference between: String = [97,98,99] and ListOfStrings = [[97], [98], [99]]:
40> String = [97,98,99].
"abc"
41> hd(String).
97
42> ListOfStrings = [[97], [98], [99]].
["a","b","c"]
43> hd(ListOfStrings).
"a"
lists:seq() returns the equivalent of String.
Response to comment:
See lists:keyfind/3:
20> TargetRecord = lists:keyfind("xx", 3, Records).
#codes{letter = "C",braille = "xx"}
21> rr(my). %Read record definition contained in my.erl into the shell.
[codes]
22> TargetRecord#codes.letter.
"C"
The string "C" is actually the list [67], which the shell has decided to display as "C". You need to be able to look up an ascii table to discover what a string really represents in Erlang. Or, you can get the ascii code of a character like this:
24> $C.
67
25> $a.
97
So, "C" is actually the list [67], and "a" is actually the list [97].

It is late to post this, nevertheless I do it because I think that the type of variable you want to use to store the alphabet is not really appropriate to the different usages you may have. I wrote a small code to show the usage of two maps (one for decoding, one for encoding). Sorry there are only a few comments in the code.
-module (braille).
-export ([test/1,show_maps/0]).
test(Str) ->
% build the conversion map
{Alphabet,Braille} = init(),
% transform a string into braille and print it
Lines = print_braille(Str,Alphabet),
% transform the braille result into string , check the result and print it
Str = read_braille(Lines,Braille),
ok.
show_maps() ->
{Alphabet,Braille} =init(),
io:format("Alphabet = ~n~100p~n~nBraille = ~n~100p~n",[Alphabet,Braille]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% creates one map to allow the conversion from latin letter to braille: Alphabet
% and one map to allow the conversion from braille to latin : Braille
% This should be the init function of a conversion server, in charge to maintain the conversion maps
init() ->
L1="x.x.xxxxx.xxxxx..x.xx.x.xxxxx.xxxxx..x.xx.x..xxxxxx.",
L2="..x....x.xx.xxxxx.xx..x....x.xx.xxxxx.xx..x.xx...x.x",
L3="....................x.x.x.x.x.x.x.x.x.x.xxxx.xxxxxxx",
LL1 = split_str(L1),
LL2 = split_str(L2),
LL3 = split_str(L3),
Alphabet = init_alphabet(LL1,LL2,LL3,$a,#{$ => #{1 => "..", 2 => "..", 3=> ".."}}),
Braille = init_braille(Alphabet),
{Alphabet,Braille}.
% a tail recursive function to split the input string into packets of 2 characters
split_str_tail_recursive([],R) ->
lists:reverse(R);
split_str_tail_recursive([A,B|Rest],R) ->
split_str_tail_recursive(Rest,[[A,B]|R]).
% interface to call the recursive fuction
split_str(L) -> split_str_tail_recursive(L,[]).
init_alphabet([],[],[],_,R) ->
R;
init_alphabet([H1|T1],[H2|T2],[H3|T3],C,R) ->
init_alphabet(T1,T2,T3,C+1,maps:put(C,#{1 => H1, 2 => H2, 3=> H3},R)).
init_braille(Alphabet) ->
H = fun(K,V,AccIn) -> maps:put({maps:get(1,V),maps:get(2,V),maps:get(3,V)},K,AccIn) end,
maps:fold(H,#{},Alphabet).
%transform a latin lower cap string into 3 lines representing a braille output, print them and returns the 3 lines
print_braille(S,Alphabet) ->
Line1 = lists:flatmap(fun(C) -> maps:get(1,maps:get(C,Alphabet)) end,S),
Line2 = lists:flatmap(fun(C) -> maps:get(2,maps:get(C,Alphabet)) end,S),
Line3 = lists:flatmap(fun(C) -> maps:get(3,maps:get(C,Alphabet)) end,S),
io:format("~s~n~s~n~s~n",[Line1,Line2,Line3]),
{Line1,Line2,Line3}.
% transform a tuple of 3 lines braille representation into latin string, print it and return it
read_braille({Line1,Line2,Line3},Braille) ->
List = lists:zip3(split_str(Line1),split_str(Line2),split_str(Line3)),
Str = lists:map(fun(B) -> maps:get(B,Braille) end,List),
io:format("~s~n", [Str]),
Str.
and here the usage:
1> c(braille).
{ok,braille}
2> braille:show_maps().
Alphabet =
#{32 => #{1 => "..",2 => "..",3 => ".."},
97 => #{1 => "x.",2 => "..",3 => ".."},
98 => #{1 => "x.",2 => "x.",3 => ".."},
99 => #{1 => "xx",2 => "..",3 => ".."},
100 => #{1 => "xx",2 => ".x",3 => ".."},
101 => #{1 => "x.",2 => ".x",3 => ".."},
102 => #{1 => "xx",2 => "x.",3 => ".."},
103 => #{1 => "xx",2 => "xx",3 => ".."},
104 => #{1 => "x.",2 => "xx",3 => ".."},
105 => #{1 => ".x",2 => "x.",3 => ".."},
106 => #{1 => ".x",2 => "xx",3 => ".."},
107 => #{1 => "x.",2 => "..",3 => "x."},
108 => #{1 => "x.",2 => "x.",3 => "x."},
109 => #{1 => "xx",2 => "..",3 => "x."},
110 => #{1 => "xx",2 => ".x",3 => "x."},
111 => #{1 => "x.",2 => ".x",3 => "x."},
112 => #{1 => "xx",2 => "x.",3 => "x."},
113 => #{1 => "xx",2 => "xx",3 => "x."},
114 => #{1 => "x.",2 => "xx",3 => "x."},
115 => #{1 => ".x",2 => "x.",3 => "x."},
116 => #{1 => ".x",2 => "xx",3 => "x."},
117 => #{1 => "x.",2 => "..",3 => "xx"},
118 => #{1 => "x.",2 => "x.",3 => "xx"},
119 => #{1 => ".x",2 => "xx",3 => ".x"},
120 => #{1 => "xx",2 => "..",3 => "xx"},
121 => #{1 => "xx",2 => ".x",3 => "xx"},
122 => #{1 => "x.",2 => ".x",3 => "xx"}}
Braille =
#{{"..","..",".."} => 32,
{".x","x.",".."} => 105,
{".x","x.","x."} => 115,
{".x","xx",".."} => 106,
{".x","xx",".x"} => 119,
{".x","xx","x."} => 116,
{"x.","..",".."} => 97,
{"x.","..","x."} => 107,
{"x.","..","xx"} => 117,
{"x.",".x",".."} => 101,
{"x.",".x","x."} => 111,
{"x.",".x","xx"} => 122,
{"x.","x.",".."} => 98,
{"x.","x.","x."} => 108,
{"x.","x.","xx"} => 118,
{"x.","xx",".."} => 104,
{"x.","xx","x."} => 114,
{"xx","..",".."} => 99,
{"xx","..","x."} => 109,
{"xx","..","xx"} => 120,
{"xx",".x",".."} => 100,
{"xx",".x","x."} => 110,
{"xx",".x","xx"} => 121,
{"xx","x.",".."} => 102,
{"xx","x.","x."} => 112,
{"xx","xx",".."} => 103,
{"xx","xx","x."} => 113}
ok
3> braille:test("the quick brown fox jumps over the lazy dog").
.xx.x...xxx..xxxx...x.x.x..xxx..xxx.xx...xx.xxxx.x..x.x.x.x....xx.x...x.x.x.xx..xxx.xx
xxxx.x..xx..x.......x.xx.xxx.x..x..x....xx....x.x....xx..xxx..xxxx.x..x....x.x...x.xxx
x.......x.xx....x.....x.x..xx.....x.xx....xxx.x.x...x.xx..x...x.......x...xxxx....x...
the quick brown fox jumps over the lazy dog
ok
4>

Related

How to address nested array keys after grouping in Rails?

I have table called sales like this:
id customer_id product_id sales
1 4 190 100
2 4 190 150
3 4 191 200
4 5 192 300
5 6 200 400
What I'd like to do is, get a total on how many of each products have been bought by a customer. Therefor I perform a simple group by:
Sales.all.group(:customer_id, product_id).sum(:sales)
What this gives me is a hash with keys consisting of arrays with the combination of customer_id and product_id:
hash = {
[4, 190] => 250,
[4, 191] => 200,
[5, 192] => 300,
[6, 200] => 400
}
While this gives me the result, I'm now also looking to get the total sum of sales for each customer. I could of course write another query, but this feels redundant. Isn't there an easy way to do find all the hash array keys that start with [4, *] or something?
To get this value for a single value, you could do the following:
hash.select{|x| x[0] == 4}.values.sum
=> 450
But you could also transform the hash to a grouped one on the customer-ids.
hash.inject(Hash.new(0)) do |result, v|
# v will look like [[4, 190], 250]
customer_id = v[0][0]
result[customer_id] += v[1]
result
end
which you could also write as a one-liner
hash.inject(Hash.new(0)){|result, v| result[v[0][0]] += v[1]; result}
=> {4=>450, 5=>700}
Which can be further shortened when using each_with_object instead of inject (thanks to commenter Sebastian Palma)
hash.each_with_object(Hash.new(0)){|v,result| result[v[0][0]] += v[1]}
You can try following,
hash.group_by { |k,v| k.first }.transform_values { |v| v.map(&:last).sum }
# => {4=>450, 5=>300, 6=>400}
Try this:
hash.each_with_object(Hash.new(0)) do |(ids, value), result|
result[ids.first] += value
end
#=> { 4 => 450, 5 => 700 }
You could group the resulting hash based on customer id. Then sum all the values in the group:
hash = {
[4, 190] => 250,
[4, 191] => 200,
[5, 192] => 300,
[6, 200] => 400
}
hash.group_by { |entry| entry.shift.first }
.transform_values { |sums| sums.flatten.sum }
#=> {4=>450, 5=>300, 6=>400}
group_by groups the key-value pairs based on the customer id. shift will remove the key (leaving only the values), while first selects the customer id (since it is the first element in the array). This leaves you with an hash { 4 => [[250], [200]], 5 => [[300]], 6 => [[400]] }.
You then flatten the groups and sum all the values to get the sum for each costomer id.
Another approach would be to group by ID.
hash.
group_by { |(k, _), _| k }.
each_with_object(Hash.new{0}) do |(k, arr), acc|
acc[k] += arr.sum(&:last)
end
#⇒ {4=>450, 5=>700}

How to convert human readable number to actual number in Ruby?

Is there a simple Rails/Ruby helper function to help you convert human readable numbers to actual numbers?
Such as:
1K => 1000
2M => 2,000,000
2.2K => 2200
1,500 => 1500
50 => 50
5.5M => 5500000
test = {
'1K' => 1000,
'2M' => 2000000,
'2.2K' => 2200,
'1,500' => 1500,
'50' => 50,
'5.5M' => 5500000
}
class String
def human_readable_to_i
multiplier = {'K' => 1_000, 'M' => 1_000_000}[self.upcase[/[KM](?=\z)/]] || 1
value = self.gsub(/[^\d.]/, '')
case value.count('.')
when 0 then value.to_i
when 1 then value.to_f
else 0
end * multiplier
end
end
test.each { |k, v| raise "Test failed" unless k.human_readable_to_i == v }
Try something like this if you have an array of human readable numbers than
array.map do |elem|
elem = elem.gsub('$','')
if elem.include? 'B'
elem.to_f * 1000000000
elsif elem.include? 'M'
elem.to_f * 1000000
elsif elem.include? 'K'
elem.to_f * 1000
else
elem.to_f
end
end
Have a look here as well, you will find many Numbers Helpers
NumberHelper Rails.
Ruby Array human readable to actual

Converting hash ruby objects to positive currency

I have a hash where the keys are the months and I want to convert the objects to positive numbers AND currency.
INPUT
hash = {
12 => -5888.969999999999,
4 => -6346.1,
3 => -6081.76,
2 => -5774.799999999999,
1 => -4454.38
}
OUTPUT
hash = {
12 => 5888.96,
4 => 6346.10,
3 => 6081.76,
2 => 5774.79,
1 => 4454.38
}
#Output should be a float
Any help would be greatly appreciated.
Try
hash.transform_values{|v| v.round(2).abs()}
or
hash.update(hash){|k,v| v.round(2).abs()}
Numeric.abs() can be applied to ensure a number is positive and Float.round(2) will round a float to 2 decimal places. See ruby-doc.org/core-2.1.4/Numeric.html#method-i-abs and ruby-doc.org/core-2.2.2/Float.html#method-i-round for usage examples. Note that round() will not add trailing zeros since that does not affect numerical value, however trailing zeros can be added by formatting, for example:
hash = {
12 => -5888.969999999999,
4 => -6346.1,
3 => -6081.76,
2 => -5774.799999999999,
1 => -4454.38
}
# transform hash values
hash.each do |key, value|
hash[key] = value.abs().round(2)
end
# print the modified hash without formatting the values
hash.each do |key, value|
puts "#{key} => #{value}"
end
# prints
# 12 => 5888.97
# 4 => 6346.1
# 3 => 6081.76
# 2 => 5774.80
# 1 => 4454.38
# print hash with values formatted with precision of 2 digits
hash.each do |key, value|
puts "#{key} => #{'%.2f' % value}"
end
# prints
# 12 => 5888.97
# 4 => 6346.10
# 3 => 6081.76
# 2 => 5774.80
# 1 => 4454.38

Convert integer to binary string, padded with leading zeros

I want to create new method Integer#to_bin that convert decimal to a binary string. The argument of #to_bin is the number of digits. The result should be padded with leading zeros to make it have that many digits.
Example:
1.to_bin(4)
#=> "0001"
1.to_bin(3)
#=> "001"
1.to_bin(2)
#=> "01"
7.to_bin(1)
#=> nil
7.to_bin
#=> "111"
etс.
What I've tried:
class Integer
def to_bin(number=nil)
if number == nil
return self.to_s(2)
else
s = self.to_s(2).size
e = number-s
one = '0'
two = '00'
three = '000'
if e==one.size
one+self.to_s(2)
elsif e==two.size
two+self.to_s(2)
elsif e==three.size
three+self.to_s(2)
end
end
end
end
How do I convert an integer to a binary string padded with leading zeros?
The appropriate way to do this is to use Kernel's sprintf formatting:
'%03b' % 1 # => "001"
'%03b' % 2 # => "010"
'%03b' % 7 # => "111"
'%08b' % 1 # => "00000001"
'%08b' % 2 # => "00000010"
'%08b' % 7 # => "00000111"
But wait, there's more!:
'%0*b' % [3, 1] # => "001"
'%0*b' % [3, 2] # => "010"
'%0*b' % [3, 7] # => "111"
'%0*b' % [8, 1] # => "00000001"
'%0*b' % [8, 2] # => "00000010"
'%0*b' % [8, 7] # => "00000111"
So defining a method to extend Fixnum or Integer is easy and cleanly done:
class Integer
def to_bin(width)
'%0*b' % [width, self]
end
end
1.to_bin(8) # => "00000001"
0x55.to_bin(8) # => "01010101"
0xaaa.to_bin(16) # => "0000101010101010"
Ruby already has a built-in mechanism to convert a number to binary: #to_s accepts a base to convert to.
30.to_s(2) # => "11110"
If you want to left-pad it with zeroes:
30.to_s(2).rjust(10, "0") => "0000011110"
You could extend this into a little method that combines the two:
class Fixnum
def to_bin(width = 1)
to_s(2).rjust(width, "0")
end
end
> 1234.to_bin
=> "10011010010"
> 1234.to_bin(20)
=> "00000000010011010010"

Number to English Word Conversion Rails

Anybody knows the method to convert the numericals to english number words in rails?
I found some Ruby scripts to convert numbericals to english words for corresponding words.
Instead of writing a script in ruby, i feel that direct function is available.
Eg. 1 -> One, 2 -> Two.
Use the numbers_and_words gem, https://github.com/kslazarev/numbers_and_words
The humanize gem that does exactly what you want:
require 'humanize'
23.humanize # => "twenty three"
0.42.humanize(decimals_as: :digits) # => "zero point four two"
No, you have to write a function yourself. The closest thing to what you want is number_to_human, but that does not convert 1 to One.
Here are some URLs that may be helpful:
http://codesnippets.joyent.com/posts/show/447
http://raveendran.wordpress.com/2009/05/29/ruby-convert-number-to-english-word/
http://deveiate.org/projects/Linguistics/
You can also use the to_words gem.
This Gem converts integers into words.
e.g.
1.to_words # one ,
100.to_words # one hundred ,
101.to_words # one hundred and one
It also converts negative numbers.
How about this? Written for converting numbers to words in the Indian system, but can be easily modified.
def to_words(num)
numbers_to_name = {
10000000 => "crore",
100000 => "lakh",
1000 => "thousand",
100 => "hundred",
90 => "ninety",
80 => "eighty",
70 => "seventy",
60 => "sixty",
50 => "fifty",
40 => "forty",
30 => "thirty",
20 => "twenty",
19=>"nineteen",
18=>"eighteen",
17=>"seventeen",
16=>"sixteen",
15=>"fifteen",
14=>"fourteen",
13=>"thirteen",
12=>"twelve",
11 => "eleven",
10 => "ten",
9 => "nine",
8 => "eight",
7 => "seven",
6 => "six",
5 => "five",
4 => "four",
3 => "three",
2 => "two",
1 => "one"
}
log_floors_to_ten_powers = {
0 => 1,
1 => 10,
2 => 100,
3 => 1000,
4 => 1000,
5 => 100000,
6 => 100000,
7 => 10000000
}
num = num.to_i
return '' if num <= 0 or num >= 100000000
log_floor = Math.log(num, 10).floor
ten_power = log_floors_to_ten_powers[log_floor]
if num <= 20
numbers_to_name[num]
elsif log_floor == 1
rem = num % 10
[ numbers_to_name[num - rem], to_words(rem) ].join(' ')
else
[ to_words(num / ten_power), numbers_to_name[ten_power], to_words(num % ten_power) ].join(' ')
end
end
You may also want to check gem 'rupees' - https://github.com/railsfactory-shiv/rupees to convert numbers to indian rupees (e.g. in Lakh, Crore, etc)

Resources