Empty range between strings representing numbers - ruby

Here are two range values.
('1'..'10').to_a => ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
but,
('2'..'10').to_a => []
Why does the second one return an empty array in ruby 1.8.7?

The issue is that Ruby 1.8 is trying to determine whether ('2'..'10') is a valid range as part of its analysis of the code, and deciding that it's invalid. '2' sorts after '10' making the range values backwards.
This is a common problem when people try to sort string values, and they'll encounter it again and again until they learn that trick never works.
Using 1.8.7:
('2'..'10').to_a
=> []
('02'..'10').to_a
=> ["02", "03", "04", "05", "06", "07", "08", "09", "10"]
Or:
(2..10).map(&:to_s)
=> ["2", "3", "4", "5", "6", "7", "8", "9", "10"]
I'd probably use the last method if I needed compatibility with 1.8 and 1.9+ Rubies.

irb(main):001:0> '2' > '10'
=> true
irb(main):002:0> '1'>'10'
=> false
Because of the string comparison algorithm, range cannot be created with lower bound bigger than the upper bound.
ACTUALLY it works in 1.9.3. Strange.

That's not what I'm getting:
irb(main):001:0> ('2'..'10').to_a
=> ["2", "3", "4", "5", "6", "7", "8", "9", "10"]
Unrelated to the question, though: the correct way is:
(2..10).to_a
(I am using Ruby 1.9.3)

in ruby2.0.0-p-0 this is what I am getting :
["2", "3", "4", "5", "6", "7", "8", "9", "10"]

Related

Ruby array map(&:flatten) is causing a latency issue

I have a multi-dimensional array as below
arr = [["2", "3", "1"], ["5", "2", "6", "1", "4", "3"], ["2", "5", "1", "3", "6", "4"], ["2", "3", "1"], ["2", "3", "1"], ["1", "2", "3"]]
I want to generate a combination and flatten it as below.
[["2", "5", "2", "2", "2", "1"], ["2", "5", "2", "2", "2", "2"], ["2", "5", "2", "2", "2", "3"], ["2", "5", "2", "2", "3", "1"], ["2", "5", "2", "2", "3", "2"], ["2", "5", "2", "2", "3", "3"],..., ["1", "3", "4", "1", "1", "3"]]
The code is as below
comb = arr.inject(&:product)
flat_arr = comb.map(&:flatten)
flat_arr = comb.map(&:flatten) is taking around 5ms-8ms. I have many such arrays and it is causing a latency issue. Is there a way to reduce it?
You could get rid of flattening altogether,
Benchmark.bm(7) do |bm|
bm.report("inject") { n.times { arr.inject(&:product).map(&:flatten) }}
bm.report("product splat") { n.times { arr[0].product(*arr[1..-1]) } }
end
user system total real
inject 3.390163 0.003636 3.393799 ( 3.397507)
product splat 0.514577 0.000000 0.514577 ( 0.514595)
As
arr.inject(&:product).map(&:flatten) == arr[0].product(*arr[1..-1])
=> true
this GitHub is comparing I/O speed of several methods. Perhaps you might try that flat_map method to optimize your last operation.

Produce all permutations of a number's digits

I've recently solved a problem, which takes a 3-4 digit number, such as 1234, (as well as some greater numbers) and returns a sorted array of ALL the possible permutations of the numbers [1234, 1432, 4213, 2431, 3412, 3214, etc.]
I don't like my solution, because it used .times to add the numbers to the array, and thus is still fallible, and furthermore ugly. Is there a way that the numbers could be added to the array the perfect number of times, so that as soon as all the possible shufflings of the numbers have been reached, the program will stop and return the array?
def number_shuffle(number)
array = []
number = number.to_s.split(//)
1000.times{ array << number.shuffle.join.to_i}
array.uniq.sort
end
Do as below using Array#permutation:
>> a = 1234.to_s.chars
=> ["1", "2", "3", "4"]
>> a.permutation(4).to_a
=> [["1", "2", "3", "4"],
["1", "2", "4", "3"],
["1", "3", "2", "4"],
["1", "3", "4", "2"],
["1", "4", "2", "3"],
["1", "4", "3", "2"],
["2", "1", "3", "4"],
["2", "1", "4", "3"],...]
Your modified method will be :
def number_shuffle(number,size)
number.to_s.chars.permutation(size).to_a
end
For your method it will be:
def number_shuffle(number)
a = number.to_s.chars
a.permutation(a.size).to_a.map { |b| b.join.to_i }
end
p number_shuffle 123 # => [123, 132, 213, 231, 312, 321]

How to access a hash that has a key that's an array and a value that's an integer

So I have a hash that looks like:
hash = { ["1", "2", "3"]=>"a", ["4", "5", "6"]=>"b", ["7", "8", "9"]=>"c" }
Though when I try to do something like hash[0] just a new line in my console shows up and if I try hash[0][0] it pops me an error that says [] method is undefined.
Now I'm wondering how to I access this in a way that I can do something like hash["1"] and it'll return me the "a".
I assume that since it lets me make hashes in this way I can access the content inside.
I'm not sure why you would want to create a hash with a key that's an array, but it works :)
hash = { ["1", "2", "3"]=>"a", ["4", "5", "6"]=>"b", ["7", "8", "9"]=>"c" }
hash[["1", "2", "3"]]
=> "a"
You might want to consider the opposite:
hash = { "a"=>["1", "2", "3"], "b"=>["4", "5", "6"], "c"=>["7", "8", "9"] }
hash["a"]
=> ["1", "2", "3"]
There's not a direct built-in way to access something like this, but by using select you can filter out the key/value pair that has the "1" and get the value for it:
hash.select { |key| key.include?("1") }.values.first
This assumes that each integer only exists in a single key.

How do I quickly reorder a Ruby Array given an order?

I have an array of values, and an array which determines the order.
How can I quickly re-arrange the array in the given order?
data = ['0','1','2','3','4','5']
order = [3,1,2,0,4,5]
I want:
data = ['3','1','2','0','4','5']
You can use the values_at method written for this kind of task:
data = ['0','1','2','3','4','5']
order = [3,1,2,0,4,5]
data.values_at *order
# => ["3", "1", "2", "0", "4", "5"]
data = ["0", "1", "2", "3", "4", "5"]
order = [3, 1, 2, 0, 4, 5]
> order.map{|x| data[x]}
=> ["3", "1", "2", "0", "4", "5"]
If you are not sure if the indices are correct, you can do this:
> order.map{|x| data.fetch(x)} # will raise an exception if index out of bounds
=> ["3", "1", "2", "0", "4", "5"]
Not as good as #Jakub's answer using Array#values_at (which I would argue should be the accepted answer) but here are some other fun alternatives:
p data.sort_by.with_index{ |d,i| order[i] }
p data.zip(order).sort_by(&:last).map(&:first)

Array Semi-Flattening

Want to convert this:
[["1", "2", "3"], ["4", "5", "6"]]
to this:
["1", "2", "3"], ["4", "5", "6"]
to be passed into Array.product(), and the first array can contain an unknown number of other arrays. for example, the array given may also be
[["1", "2", "3"], ["4", "5", "6"], ["7", "8", "9"]]
And ultimately, I need to pass the argument as:
otherArray.product(["1", "2", "3"], ["4", "5", "6"], ["7", "8", "9"])
Thanks ahead of time!
otherArray.product(*[["1", "2", "3"], ["4", "5", "6"], ["7", "8", "9"]]);
* is used in argument list to unpack array contents to arguments (like
here) or to pack arguments into an array,like in "def mymethod(*args)"
Reference: http://www.justskins.com/forums/apply-method-to-array-17387.html
I think what would work for you is using Ruby's Array expansion:
a=[[1,2,3],[4,5,6]]
b=[1,2,3].product([1,2,3],[4,5,6])
c=[1,2,3].product(*a)
b == c #This should be true
Basically putting the asterisk (*) in front of the variable will expand all elements in the array into a list of arguments, which is what you want.
The last line of code aside, the rest of it seems to be solved by using the 0 index:
arr[0]

Resources