Sort array of numbers in Scientific Notation - ruby

I would like to sort an array of numbers (in scientific notation) from the smallest to the highest.
This is what I have tried (in vain):
require 'bigdecimal'
s = ['1.8e-101','1.3e-116', '0', '1.5e-5']
s.sort { |n| BigDecimal.new(n) }.reverse
# Results Obtained
# => [ "1.3e-116", "1.8e-101", "0", "1.5e-5" ]
# Expected Results
# => [ "0", "1.3e-116", "1.8e-101", "1.5e-5"]

The block of Enumerable#sort is expected to return -1, 0 or 1. What you want is Enumerable#sort_by:
s.sort_by { |n| BigDecimal.new(n) }
# => ["0", "1.3e-116", "1.8e-101", "1.5e-5"]

Another option is to use BigDecimal#<=> within sort:
s.sort { |x, y| BigDecimal(x) <=> BigDecimal(y) }
#=> ["0", "1.3e-116", "1.8e-101", "1.5e-5"]

Related

Creating a Ruby Hash Map in a Functional Way

I have an array I want to turn into a hash map keyed by the item and with an array of indices as the value. For example
arr = ["a", "b", "c", "a"]
would become
hsh = {"a": [0,3], "b": [1], "c": [2]}
I would like to do this in a functional way (rather than a big old for loop), but am a little stuck
lst = arr.collect.with_index { |item, i| [item, i] }
produces
[["a", 0], ["b", 1], ["c", 2], ["a", 3]]
I then tried Hash[lst], but I don't get the array in the value and lose index 0
{"a"=>3, "b"=>1, "c"=>2}
How can I get my desired output in a functional way? I feel like it's something like
Hash[arr.collect.with_index { |item, i| [item, item[i] << i || [i] }]
But that doesn't yield anything.
Note: Trying to not do it this way
hsh = {}
arr.each.with_index do |item, index|
if hsh.has_key?(item)
hsh[item] << index
else
hsh[item] = [index]
end
end
hsh
Input
arr = ["a", "b", "c", "a"]
Code
p arr.map
.with_index
.group_by(&:first)
.transform_values { |arr| arr.map(&:last) }
Output
{"a"=>[0, 3], "b"=>[1], "c"=>[2]}
I would like to do this in a functional way (rather than a big old for loop), but am a little stuck
lst = arr.collect.with_index { |item, i| [item, i] }
produces
[["a", 0], ["b", 1], ["c", 2], ["a", 3]]
This is very close. The first thing I would do is change the inner arrays to hashes:
arr.collect.with_index { |item, i| { item => i }}
#=> [{ "a" => 0 }, { "b" => 1 }, { "c" => 2 }, { "a" => 3 }]
This is one step closer. Now, actually we want the indices in arrays:
arr.collect.with_index { |item, i| { item => [i] }}
#=> [{ "a" => [0] }, { "b" => [1] }, { "c" => [2] }, { "a" => [3] }]
This is even closer. Now, all we need to do is to merge those hashes into one single hash. There is a method for that, which is called Hash#merge. It takes an optional block for deconflicting duplicate keys, and all we need to do is concatenate the arrays:
arr.collect.with_index { |item, i| { item => [i] }}.inject({}) {|acc, h| acc.merge(h) {|_, a, b| a + b } }
#=> { "a" => [0, 3], "b" => [1], "c" => [2] }
And we're done!
How can I get my desired output in a functional way? I feel like it's something like
Hash[arr.collect.with_index { |item, i| [item, item[i] << i || [i] }]
But that doesn't yield anything.
Well, it has a SyntaxError, so obviously if it cannot even be parsed, then it cannot run, and if it doesn't even run, then it cannot possibly yield anything.
However, not that even if it worked, it would still violate your constraint that it should be done "in a functional way", because Array#<< mutates its receiver and is thus not functional.
arr.map.with_index.each_with_object({}){ |(a, i), h| h[a] ? h[a] << i : (h[a] = [i]) }
#=> {"a"=>[0, 3], "b"=>[1], "c"=>[2]}
arr.map.with_index => gives enumeration of each element with it's index
each_with_object => lets you reduce the enumeration on a provided object(represented by h in above)

Is there a one liner or more efficient way to get this done?

Given a hash, for example:
hash = { "fish" => 2, "cat" => 3, "dog" => 1 }
I need to get:
A comma separated string for all the values, E.g. "2,3,1"
An integer that hold the total sum of the values, E.g. 6
My current code:
value_string = hash.map { |k,v| "#{v}"}.join(',')
sum = 0
hash.map { |k,v| sum += v}
You can do it like this:
hash.values.join(",") # => "2,3,1"
hash.values.inject(:+) # => 6
Here is a solution to compute both the values in single iteration (some sort of one liner)
r1,r2 = hash.reduce([nil, 0]){|m,(_,v)| [[m[0], v.to_s].compact.join(","),m[-1]+v]}
#=> ["2,3,1", 6]

Array elements to integers

I have an array of strings containing numbers:
array = ["1", "2", "3"]
I want to convert every string in the array to an integer.
array.each { |n| n.to_i } does not work, because
p array.inject(:+)
returns "123" (string) rather than 6 (integer)
array = ["1", "2", "3"]
new_array = array.map { |n| n.to_i }
p new_array.inject(:+)
=> 6
one-line solution:
array.map(&:to_i).inject(:+)
# => 6

How to split a hash in two hashes based on a condition?

I have a hash:
input = {"a"=>"440", "b"=>"-195", "c"=>"-163", "d"=>"100"}
From it I want to get two hashes, one containing the pairs whose value (as integer) is positive, the other containing negative values, for example:
positive = {"a"=>"440", "d"=>"100" }
negative = {"b"=>"-195", "c"=>"-163" }
How can I achieve this using the minimum amount of code?
You can use the Enumerable#partition method to split an enumerable object (like a hash) based on a condition. For example, to separate positive/negative values:
input.partition { |_, v| v.to_i < 0 }
# => [[["b", "-195"], ["c", "-163"]],
# [["a", "440"], ["d", "100"]]]
Then, to get the desired result, you can use map and to_h to convert the key/value arrays to hashes:
negative, positive = input.partition { |_, v| v.to_i < 0 }.map(&:to_h)
positive
# => {"a"=>"440", "d"=>"100"}
negative
# => {"b"=>"-195", "c"=>"-163"}
If you use a version of Ruby prior 2.1 you can replace the Array#to_h method (that was introduced in Ruby 2.1) like this:
evens, odds = input.partition { |_, v| v.to_i.even? }
.map { |alist| Hash[alist] }
This implementation uses Enumerable#group_by:
input = {"a"=>"440", "b"=>"-195", "c"=>"-163", "d"=>"100"}
grouped = input.group_by { |_, v| v.to_i >= 0 }.map { |k, v| [k, v.to_h] }.to_h
positives, negatives = grouped.values
positives #=> {"a"=>"440", "d"=>"100"}
negatives #=> {"b"=>"-195", "c"=>"-163"}
I must say that Enumerable#partition is more appropriate, as #toro2k answered.
something like this then?
positive = Hash.new
negative = Hash.new
input.each_pair { |var,val|
if val.to_i > 0
positive[var] = val
else
negative[var] = val
end
}

Simple ruby array smallest integer?

This is my array : array = ["1", "Hel", "6", "3", "lo" ]I want to output the smallest number in the array. Then I want to output the largest number in the array? How do I achieve this? Thanks!
Well it depends how you want to handle the string elements that aren't easily parsed into numbers. Like "Hel" and "lo".
If you do this:
array.map {|x| Integer(x) rescue nil }.compact.min
array.map {|x| Integer(x) rescue nil }.compact.max
Then you'll ignore those, which is probably the right thing, assuming you don't have some reason for considering "Hel" and "lo" to have numerical values.
numbers = array.select { |x| x[/^-?\d+$/] }.map(&:to_i)
# => [1, 6, 3]
numbers.min
# => 1
numbers.max
# => 6
Another variation to work with negative numbers
smalles, largest =
["1", "Hel", "6", "3", "lo","-9" ].select { |x| x[/^-?\d+$/] }.minmax_by(&:to_i)
smallest # => -9 largest # => 6
smallest, largest =
["1", "Hel", "6", "3", "lo" ].reject{|s| s =~ /\D/}.minmax_by(&:to_i)
smallest # => "1"
largest # => "6"
Another way:
array.join(',').scan(/-?\d+/).minmax_by(&:to_i)
#=> ["-4", "6"]
we can use unicode [[:digit:]] instead writing regular expression as
array.join(',').scan(/[[:digit:]]/).minmax_by(&:to_i)

Resources