Multiply all even indexed integers by two - ruby

Wanting to take a fixnum of integers and multiply all even(indexed) integers by two. I figured the best way to do this is first turn fixnum into an array. So lets say the following number of 16 digits: a = 4408041234567901
I know I could:
a.to_s.split('')
Which will return 'a' to an array of 'stringed' numbers. But then I cant follow up with:
a.map!.with_index {|i,n| i.even? n*2}
Guess I'm kinda stuck on how to create a method to do this. So my question may even be how to turn that group of numbers into an array of fixnums/integers instead of strings.

I would prefer to remove the conditional altogether from the loop, by creating an Enumerator that contains the coefficients you want to multiply by (2 for even indexes and 1 for odd.)
coef = [2, 1].cycle
This essentially creates an Enumerator that alternately returns 2 and 1 when next is called on it. You can then use this to simplify your map to:
a.to_s.each_char.map { |v| v.to_i * coef.next }

To change it to an Array, you could do
a = 4408041234567901
arr = a.to_s.chars.map(&:to_i)
# => [4, 4, 0, 8, 0, 4, 1, 2, 3, 4, 5, 6, 7, 9, 0, 1]
You can also multiply alternate numbers by 2
arr = a.to_s.chars.map.with_index {|n,i| i.even? ? n.to_i * 2 : n.to_i }
# => [8, 4, 0, 8, 0, 4, 2, 2, 6, 4, 10, 6, 14, 9, 0, 1]
Improving a little bit, you can use a Hash to find the number to be multiplied.
h = {true => 2, false => 1}
a.to_s.each_char.map.with_index {|n,i| n.to_i * h[i.even?]}
EDIT
I can explain each step, But it will be better if you can try to figure it out on your own. Open irb, type a.to_s and check the output. Then type a.to_s.chars and inspect the output and so on..

a = 4408041234567901
even_odd = [:even, :odd].cycle
#=> #<Enumerator: [:even, :odd]:cycle>
If the indexing starts with the highest-order (leftmost) digit:
a.to_s.each_char.map { |d|
(even_odd.next == :even) ? 2*d.to_i : d.to_i }
#=> [8, 4, 0, 8, 0, 4, 2, 2, 6, 4, 10, 6, 14, 9, 0, 1]
If the indexing starts with the ones digit:
s = a.to_s
even_odd.next if s.size.even?
s.each_char.map { |d| ( even_odd.next == :even) ? 2*d.to_i : d.to_i }
#=> [4, 8, 0, 16, 0, 8, 1, 4, 3, 8, 5, 12, 7, 18, 0, 2]
Here are the steps for the example when zero-based indexing starts with the highest-order digit.
Array#cycle converts the array [:even, :odd] to an enumerator:
even_odd = [:even, :odd].cycle
even_odd.next #=> :even
even_odd.next #=> :odd
even_odd.next #=> :even
even_odd.next #=> :odd
...
b = a.to_s
#=> "4408041234567901"
enum0 = b.each_char
#=> #<Enumerator: "4408041234567901":each_char>
The enumerator enum0 passes the digits of b to map. I could have instead written:
b = a.to_s.chars
# => ["4", "4", "0", "8", "0", "4", "1", "2",
# "3", "4", "5", "6", "7", "9", "0", "1"]
but that creates an intermediate array. The enumerator does not and therefore is more efficient. Continuing...
enum1 = enum0.map
#=> #<Enumerator: #<Enumerator: "4408041234567901":each_char>:map>
You can think of this as a "compound enumerator". We can see its contents by converting it to an array:
enum1.to_a
#=> ["4", "4", "0", "8", "0", "4", "1", "2",
# "3", "4", "5", "6", "7", "9", "0", "1"]
The method each will pass each element of the enumerator into the block. Proof:
enum1.each { |d| (enum.next == :even) ? 2*d.to_i : d.to_i }
# => [8, 4, 0, 8, 0, 4, 2, 2, 6, 4, 10, 6, 14, 9, 0, 1]
We can manually step through the elements of enum1 by using Enumerator#next. We will assign the value to the block variable d and perform the calculation in the block to map the digit d:
d = enum1.next
#=> "4"
(enum.next == :even) ? 2*d.to_i : d.to_i
#=> (:even == :even) ? 2*"4".to_i : "4".to_i
#=> (true) ? 8 : 4
#=> 8 ("4" is mapped to 8)
d = enum1.next
#=> "4"
(enum.next == :even) ? 2*d.to_i : d.to_i
#=> (:odd == :even) ? 2*"4".to_i : "4".to_i
#=> (false) ? 8 : 4
#=> 4 ("4" is mapped to 4)
d = enum1.next
#=> "0"
#=> (:even == :even) ? 2*"0".to_i : "0".to_i
#=> (true) ? 0 : 0
#=> 8 ("0" is mapped to 0)
and so on.

Related

How to split a String in a multidimensional Array in ruby?

I have an multidimensional Array in Ruby, like this:
[["2013-08-22 13:23:12 +0212", 100, 1], ["2013-09-22 14:25:12 +0123" , 123, 1]]
I would like to split the string in the first array position and the time to hours, minutes and seconds (and convert these to integers), so it will become:
[["2013-08-22", 13, 23, 12, "+0212", 100, 1], [.....]]
Does anyone know how to solve this problem?
If you would like more readability, I suggest this:
require 'date'
array = [["2013-08-22 13:23:12 +0212", 100, 1], ["2013-09-22 14:25:12 +0123" , 123, 1]]
array.map do |date_time, a, b|
date_time = DateTime.parse(date_time)
[
date_time.strftime('%F'), date_time.hour, date_time.min,
date_time.sec, date_time.strftime('%z'), a, b
]
end
It makes it very clear what each element of the resulting array is. You should replace a and b with meaningful names if you have them.
[["2013-08-22 13:23:12 +0212", 100, 1],
["2013-09-22 14:25:12 +0123" , 123, 1]].
map do |arr|
arr.shift.split(/[: ]/) + arr # first position only
# ⇓ this will convert hours, minutes and seconds to integers
# arr.shift.split(/[: ]/).map { |e| e[/\A\d+\z/] ? e.to_i : e } + arr
# ⇓ this would work for all strings
# arr.flat_map { |e| e.is_a?(String) ? e.split(/[: ]/) : e }
end
#⇒ [
# [0] [
# [0] "2013-08-22",
# [1] "13",
# [2] "23",
# [3] "12",
# [4] "+0212",
# [5] 100,
# [6] 1
# ],
# [1] [
# [0] "2013-09-22",
# [1] "14",
# [2] "25",
# [3] "12",
# [4] "+0123",
# [5] 123,
# [6] 1
# ]
# ]
Here's another option (converting hours, minutes and seconds to integers):
arr = [["2013-08-22 13:23:12 +0212", 100, 1], ["2013-09-22 14:25:12 +0123" , 123, 1]]
arr.map do |item|
date, time, zone = item[0].split.map { |e| e.split(":") }
[date, time.map(&:to_i), zone, item[1..-1]].flatten
end
#=> [["2013-08-22", 13, 23, 12, "+0212", 100, 1], ["2013-09-22", 14, 25, 12, "+0123", 123, 1]]
Non-destructive way. ref #mudasobwa answer
arr = [["2013-08-22 13:23:12 +0212", 100, 1], ["2013-09-22 14:25:12 +0123" , 123, 1]]
arr.map {|e| e[0].split(/[ :]/) + e[1..-1] }
#=> [["2013-08-22", "13", "23", "12", "+0212", 100, 1], ["2013-09-22", "14", "25", "12", "+0123", 123, 1]]

Recursive solution doesn't iterate correctly

I'm working through a toy problem in Ruby: how to produce all possible 10-digit phone numbers where each successive number is adjacent to the last on the keypad. I've represented the adjacent relationships between numbers, and have a recursive function, but my method isn't iterating through the whole solution space. It's just finding the first solution and returning.
Here's my code:
adjacencies = { 1 => [2, 4],
2 => [1, 3, 5],
3 => [2, 6],
4 => [1, 5, 7],
5 => [2, 4, 6, 8],
6 => [3, 5, 9],
7 => [4, 8],
8 => [5, 7, 9, 0],
9 => [6, 8],
0 => [8]
}
def append_number(partial_phone_number, to_append, adjacencies)
phone_length = 10
partial_phone_number = partial_phone_number + to_append.to_s
if (partial_phone_number.length == phone_length)
return partial_phone_number
else
adjacencies[to_append].each do |a|
return append_number(partial_phone_number, a, adjacencies)
end
end
end
(0..9).each do |n|
puts append_number("", n, adjacencies)
end
And here is the output when I run it:
0852121212
1212121212
2121212121
3212121212
4121212121
5212121212
6321212121
7412121212
8521212121
9632121212
The first time you enter the adjacencies[to_append].each, you immediately return from the method, therefore the loop will never be executed more than once.
You need to
return a list of phone numbers instead of just a single phone number
build that list somehow in your recursive call
Here is a modification of your recursive method. FIRST_DIGIT is an array of possible first digits of an n-digit phone number, n being the first argument of the method recurse. You wish to determine recurse(10).
ADJ = { 1 => [2, 4],
2 => [1, 3, 5],
3 => [2, 6],
4 => [1, 5, 7],
5 => [2, 4, 6, 8],
6 => [3, 5, 9],
7 => [4, 8],
8 => [5, 7, 9, 0],
9 => [6, 8],
0 => [8]
}
FIRST_DIGIT = (1..9).to_a
#=> [1, 2, 3, 4, 5, 6, 7, 8, 9]
def recurse(n, nxt=FIRST_DIGIT)
nxt.each_with_object([]) do |i,a|
is = i.to_s
if n==1
a << is
else
recurse(n-1, ADJ[i]).each { |s| a << is + s }
end
end
end
recurse 1
#=> ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
recurse 2
#=> ["12", "14", "21", "23", "25", "32", "36", "41", "45",
# "47", "52", "54", "56", "58", "63", "65", "69",
# "74", "78", "85", "87", "89", "80", "96", "98"]
recurse 3
#=> ["121", "123", "125", "141", "145", "147",
# "212", "214", "232", "236", "252", "254", "256", "258",
# "321", "323", "325", "363", "365", "369",
# "412", "414", "452", "454", "456", "458", "474", "478",
# "521", "523", "525", "541", "545", "547", "563", "565",
# "569", "585", "587", "589", "580",
# "632", "636", "652", "654", "656", "658", "696", "698",
# "741", "745", "747", "785", "787", "789", "780",
# "852", "854", "856", "858", "874", "878", "896", "898", "808",
# "963", "965", "969", "985", "987", "989", "980"]
recurse(10).size
#=> 117529
[Edit: the OP has asked about the possibility of modifying the code to avoid loops. This would not be difficult. The same modification could be used to enforce other rules as well (e.g, no 666), all of which would reduce the numbers of combinations to be considered. We could do this by adding an argument so_far to recurse that is an array (or it could be a string) of all digits selected so far:
def recurse(n, so_far=[], nxt=FIRST_DIGIT)
nxt.each_with_object([]) do |i,a|
is = i.to_s
if n==1
a << is
else
< construct array 'permitted' from ADJ[i] and other rules >
recurse(n-1, so_far+[i], permitted).each { |s| a << is + s }
end
end
end
Note that having two arguments with defaults is not a problem, as recurse will initially be called with only the first argument and thereafter will be called with all three arguments.
The return statement in the each iterator bails out of the recursive call on the first iteration. Do not use return there. One possible solution is to append the result to a list (which you pass by argument) when you get to the recursive base case.

Creating a method to modify elements in an array in Ruby with 2 arguments, one of them being the original array

I would like to create a method, mod_method(array, n) , where array is an array and n is a number. mod_method should take number n and add it to all internal numbers in array and return that new array.
For example, using array = ["I", "have", 3, "to", 4, "hours"], how would I find mod_method(array, 1) such that
mod_method(array,1)
=> ["I", "have", 4, "to", 5, "hours"]
I'm a noob and was only able to do this using the already defined array and number (let's use 1), as such:
array = ["I", "have", 3, "to", 4, "hours"]
=>[[0] "I",
[1] "have",
[2] 3,
[3] "to",
[4] 4,
[5] "hours"]
numbers = array.values_at(2, 4)
=> [
[0] 3,
[1] 4
mod = numbers.map{|x| x + 1}
=> [
[0] 4,
[1] 5]
new_array = ["I", "have", mod[0], "to", mod[1], "hours"]
=> ["I", "have", 4, "to", 5, "hours"]
I have no idea how to do it with undefined arguments for the mod_method.
Write the method as
def mod_method(array, n)
array.map { |i| i.is_a?(Fixnum) ? (i + n) : i }
end
array = ["I", "have", 3, "to", 4, "hours"]
mod_method(array, 1) # => ["I", "have", 4, "to", 5, "hours"]
If your array contains both Fixnum and Float instances, and you want to add 1 with either of those instances. Then use the below method :-
def mod_method(array, n)
array.map { |i| i.kind_of?(Numeric) ? (i + n) : i }
end
array = ["I", "have", 3.2, "to", 4, "hours"]
mod_method(array, 1) # => ["I", "have", 4.2, "to", 5, "hours"]
Here is what I would do.
def mod_method(array,n)
array.map do |e|
e.is_a?(Integer) ? e + n : e
end
end

Sorting alpha-numeric array?

I would like to sort an alphanumeric array in Ruby and return a sorted array back.
For example:
["world", "jim", 4, 1, "apple"]
to return:
["apple", "jim", 1, 4, "world"]
so that where there is an object of the same class in the same position before the sort, just now it's numerical/alphabetical.
xs = ["world", "jim", 4, 1, "apple", 5, 6]
sorted_by_cls = xs.group_by(&:class).each { |k,vs| vs.sort!.reverse! }
sorted_xs = xs.map(&:class).map { |c| sorted_by_cls[c].pop }
Don't know how this compares to other solutions, but here's another one:
xs = ["world", "jim", 4, 1, "apple", 5, 6]
classes = xs.map(&:class)
sorts = Hash[*classes.uniq.map {|c| [c, xs.select {|x| x.class == c}.sort]}.flatten(1)]
classes.map {|c| sorts[c].shift} # => ["apple", "jim", 1, 4, "world", 5, 6]
A very interesting problem. Here's how I approached it assuming an alphanumeric array. Partition the array into alpha and numeric subarrays, sort them, and reconstitute based on the position and class of the object in the original array.
arr = ["world", "jim", 4, 1, "apple"]
alpha_num = arr.partition { |l| l.is_a? String }.map(&:sort)
arr.map { |l| l.is_a?(String) ? alpha_num.first.shift : alpha_num.last.shift }
My generalized solution is not much different than that of FMc:
arr = ["world", "jim", 4, 1, "apple", 5, 6]
sorted_hash = arr.group_by(&:class).each_value(&:sort!)
arr.map { |l| sorted_hash[l.class].shift }
arr = ["world", "jim", 4, 1, "apple"]
arr.each_with_index.group_by {|e| e.first.class}.values.map {|g|\
g.map(&:first).sort.zip(g.map(&:last).sort)}\
.each_with_object(Array.new(arr.size)) {|e,a| e.each {|f,i| a[i] = f}}
# => ["apple", "jim", 1, 4, "world"]
Let's go through this:
a1 = arr.each.with_index.group_by {|e| e.first.class} # => {String=>[["world", 0], ["jim", 1], ["apple", 4]], Fixnum=>[[4, 2], [1, 3]]}
a21, a22 = a1.values #=>[[["world",0],["jim",1],["apple",4]],[[4,2],[1,3]]]
a31 = a21.map(&:first) # => ["world", "jim", "apple"]
a41 = a31.sort # => ["apple", "jim", "world"]
a51 = a21.map(&:last).sort # => [0, 1, 4]
a61 = a41.zip(a51) # => [["apple", 0], ["jim", 1], ["world", 4]]
a62 = a22.map(&:first).sort.zip(a22.map(&:last).sort) # => [[1,2], [4,3]]
[a61, a62].each_with_object(Array.new(5)) {|e,a| e.each {|f,i| a[i] = f}}
a = ["world", "jim", 4, 1, "apple", "cabbage"]
is = a.each_index.group_by{|i| a[i].class}.values
.flat_map{|is| [is, is.sort_by{|i| a[i]}].transpose}
.sort.map(&:last)
a.values_at(*is)

To find the integer (Fixnum) values in ruby array

I have an array [1, 2, "3", "4", "1a", "abc", "a"] with
pure integers (1, 2),
string formatted integers ("1", "2"),
strings ("a", "b"), and
mixed string numbers ("1a", "2s").
From this, I need to pick up only the integers (including string formatted) 1, 2, "3", "4".
First I tried with to_i:
arr = [1, 2, "3", "4", "1a", "abc", "a"]
arr.map {|x| x.to_i}
# => [1, 2, 3, 4, 1, 0, 0]
but this one converts "1a" to 1, which I don't expect.
Then I tried Integer(item):
arr.map {|x| Integer(x) } # and it turned out to be
# => ArgumentError: invalid value for Integer(): "1a"
Now I am out of straight conversion options here. Finally, I decided to do this way, which converts the value to_i and to_s. So "1" == "1".to_i.to_s is an integer, but not "1a" == "1a".to_i.to_s and "a" == "a".to_i.to_s
arr = arr.map do |x|
if (x == x.to_i.to_s)
x.to_i
else
x
end
end
and
ids, names= arr.partition { |item| item.kind_of? Fixnum }
Now I got the arrays of integers and strings. Is there a simple way to do this?
Similar solution as provided by #maerics, but a bit slimmer:
arr.map {|x| Integer(x) rescue nil }.compact
class Array
def to_i
self.map {|x| begin; Integer(x); rescue; nil; end}.compact
end
end
arr = [1, 2, "3", "4", "1a", "abc", "a"]
arr.to_i # => [1, 2, 3, 4]
something like this:
a = [1,2,"3","4","1a","abc","a"]
irb(main):005:0> a.find_all { |e| e.to_s =~ /^\d+$/ }.map(&:to_i)
=> [1, 2, 3, 4]
Hey, thanks awakening my ruby. Here is my go at this problem:
arr=[1,2,"3","4","1a","abc","a"]
arr.map {|i| i.to_s}.select {|s| s =~ /^[0-9]+$/}.map {|i| i.to_i}
//=> [1, 2, 3, 4]
I noticed most of the answer so far changes the value of "3" and "4" to actual integers.
>> array=[1, 2, "3", "4", "1a", "abc", "a", "a13344a" , 10001, 3321]
=> [1, 2, "3", "4", "1a", "abc", "a", "a13344a", 10001, 3321]
>> array.reject{|x| x.to_s[/[^0-9]/] }
=> [1, 2, "3", "4", 10001, 3321]
#OP, I have not tested my solution exhaustively, but so far it seems to work (of course its done according to provided sample ), so please test thoroughly yourself.
How about this?
[1,2,"3","4","1a","abc","a"].select{|x| x.to_i.to_s == x.to_s}
# => [1, 2, "3", "4"]
Looks pretty simple
arr.select{ |b| b.to_s =~ /\d+$/ }
# or
arr.select{ |b| b.to_s[/\d+$/] }
#=> [1, 2, "3", "4"]

Resources