Remove one level of a nested array - ruby

How can you change this array:
[["1","one"], ["2","two"], ["3","three"]]
to this?
["1","one"], ["2","two"], ["3","three"]
Clarification
My apologies for giving an invalid second version. This is what I'm really going for:
I want to add ["0","zero"] to the beginning of [["1","one"], ["2","two"], ["3","three"]], to get:
[["0","zero"], ["1","one"], ["2","two"], ["3","three"]]
I have tried:
["0","zero"] << [["1","one"], ["2","two"], ["3","three"]]
The above approach produces this, which contains a nesting I don't want:
[["0","zero"], [["1","one"], ["2","two"], ["3","three"]]]

unshift ought to do it for you:
a = [["1","one"], ["2","two"], ["3","three"]]
a.unshift(["0", "zero"])
=> [["0", "zero"], ["1", "one"], ["2", "two"], ["3", "three"]]

You are probably looking for flatten:
Returns a new array that is a one-dimensional flattening of this array (recursively). That
is, for every element that is an array, extract its elements into the new array. If the optional level argument determines the level of recursion to flatten.
[["1","one"], ["2","two"], ["3","three"]].flatten
Which gives you:
=> ["1", "one", "2", "two", "3", "three"]

[["1","one"], ["2","two"], ["3","three"]].flatten

You need to make your questions clearer, but is the following what you meant?
# Right answer
original = [["1","one"], ["2","two"]]
items_to_add = [["3","three"], ["4","four"]]
items_to_add.each do |item|
original << item
end
original # => [["1", "one"], ["2", "two"], ["3", "three"], ["4", "four"]]
# Wrong answer
original = [["1","one"], ["2","two"]]
items_to_add = [["3","three"], ["4","four"]]
original << items_to_add # => [["1", "one"], ["2", "two"], [["3", "three"], ["4", "four"]]]

Related

How to find union between array of first value in ruby

I have a set of array like this:
[["1","2"],["1","3"],["2","3"],["2","5"]]
I want to find the union of first values like
["1","2"],["1","3"] matches so i need to create new array like ["1","2,3"]
so the resulting array will be like
[["1","2,3"],["2","3,5"]]
Like most problems in Ruby, the Enumerable module does the job:
input = [["1","2"],["1","3"],["2","3"],["2","5"]]
result = input.group_by do |item|
# Group by first element
item[0]
end.collect do |key, items|
# Compose into new format
[
key,
items.collect do |item|
item[1]
end.join(',')
]
end
puts result.inspect
# => [["1", "2,3"], ["2", "3,5"]]
The group_by method comes in very handy when aggregating things like this, and collect is great for rewriting how the elements appear.
What you are asking for is not a true union for a true union of each 2 it would be:
data = [["1","2"],["1","3"],["2","3"],["2","5"]]
data.each_slice(2).map{|a,b| a | b.to_a }
#=> [["1","2","3"],["2","3","5"]]
Here is a very simple solution that modifies this concept to fit your needs:
data = [["1","2"],["1","3"],["2","3"],["2","5"]]
data.each_slice(2).map do |a,b|
unified = (a | b.to_a)
[unified.shift,unified.join(',')]
end
#=> [["1", "2,3"], ["2", "3,5"]]
Added to_a to piped variable b in the event that there are an uneven number of arrays. eg.
data = [["1","2"],["1","3"],["2","3"],["2","5"],["4","7"]]
data.each_slice(2).map do |a,b|
unified = (a | b.to_a)
[unified.shift,unified.join(',')]
end
#=> [["1", "2,3"], ["2", "3,5"], ["4","7"]]
If you meant that you want this to happen regardless of order then this will work but will destroy the data object
data.group_by(&:shift).map{|k,v| [k,v.flatten.join(',')]}
#=> [["1", "2,3"], ["2", "3,5"], ["4","7"]]
Non destructively you could call
data.map(&:dup).group_by(&:shift).map{|k,v| [k,v.flatten.join(',')]}
#=> [["1", "2,3"], ["2", "3,5"], ["4","7"]]
Here's another way.
Code
def doit(arr)
arr.each_with_object({}) { |(i,*rest),h| (h[i] ||= []).concat(rest) }
.map { |i,rest| [i, rest.join(',')] }
end
Examples
arr1 = [["1","2"],["1","3"],["2","3"],["2","5"]]
doit(arr1)
#=> [["1", "2,3"], ["2", "3,5"]]
arr2 = [["1","2","6"],["2","7"],["1","3"],["2","3","9","4","cat"]]
doit(arr2)
# => [["1", "2,6,3"], ["2", "7,3,9,4,cat"]]
Explanation
For arr1 above, we obtain:
enum = arr1.each_with_object({})
#=> #<Enumerator: [["1", "2"], ["1", "3"], ["2", "3"],
# ["2", "5"]]:each_with_object({})>
We can convert enum to an array see its elements:
enum.to_a
#=> [[["1", "2"], {}], [["1", "3"], {}],
# [["2", "3"], {}], [["2", "5"], {}]]
These elements will be passed into the block, and assigned to the block variables, by Enumerator#each, which will invoke Array#each. The first of these elements ([["1", "2"], {}]) can be obtained by invoking Enumerator#next on enum:
(i,*rest),h = enum.next
#=> [["1", "2"], {}]
i #=> "1"
rest #=> ["2"]
h #=> {}
We then execute:
(h[i] ||= []).concat(rest)
#=> (h["1"] ||= []).concat(["2"])
#=> (nil ||= []).concat(["2"])
#=> [].concat(["2"])
#=> ["2"]
each then passes the next element of enum to the block:
(i,*rest),h = enum.next
#=> [["1", "3"], {"1"=>["2"]}]
i #=> "1"
rest #=> ["3"]
h #=> {"1"=>["2"]}
(h[i] ||= []).concat(rest)
#=> (h["1"] ||= []).concat(["3"])
#=> (["2"] ||= []).concat(["3"])
#=> ["2"].concat(["3"])
#=> ["2", "3"]
After passing the last two elements of enum into the block, we obtain:
h=> {"1"=>["2", "3"], "2"=>["3", "5"]}
map creates an enumerator:
enum_h = h.each
#=> > #<Enumerator: {"1"=>["2", "3"]}:each>
and calls Enumerator#each (which calls Hash#each) to pass each element of enum_h into the block:
i, rest = enum_h.next
#=> ["1", ["2", "3"]]
then computes:
[i, rest.join(',')]
#=> ["1", ["2", "3"].join(',')]
#=> ["1", "2,3"]
The other element of enum_h is processed similarly.

joining multidimensional array Into pairs Ruby

For this program I'm making i need to join together some arrays in a multidimensional array:
What the array is:
[["2", "2"]["0", "9"]["2", "2"]["2", "7"]["1", "7"]["0", "8"]["0", "1"]
["0", "9"]]
And I want it to become like this:
["22", "09", "22", "27", "17", "08", "01", "09"]
Sorry if this is a really dumb question but if someone can help me I would be very happy, and if it is impossible to do this, then please tell me.
Thanks.
Try this:
multi_dimensional_array = [["2", "2"], ["0", "9"], ["2", "2"], ["2", "7"], ["1", "7"], ["0", "8"], ["0", "1"], ["0", "9"]]
multi_dimensional_array.map(&:join)
map iterates over the entries in your array and returns a copy of the array with modified entries.
&:join sends join to every member of the iteration by converting the symbol to a block using Symbol#to_proc: You can read it as if it expands to:
->(entry){ entry.send(:join) }
Generally the pattern is:
list = [["2", "2"],["0", "9"],["2", "2"],["2", "7"],["1", "7"],["0", "8"],["0", "1"],["0", "9"]]
# For each item in the list, transform it...
list.collect do |entry|
# ...by joining the bits together into a single string.
entry.join
end

How to join second or third dimension array items without affecting first dimension

I am trying to create an array that shows every digit permutation of a given number input. With a given input "123", the array should look like this:
["123", "132", "213", "231", "312", "321"]
I can get an array containing arrays of the separate digits:
a = []
"123".split('').each {|n| a.push(n) }
arraycombinations = a.permutation(a.length).to_a
# => [["1", "2", "3"], ["1", "3", "2"], ["2", "1", "3"], ["2", "3", "1"], ["3", "1", "2"], ["3", "2", "1"]]
but I cannot figure out how to join the second or third dimensions of arraycombinations while preserving the first dimension.
Each of these attempts failed:
arraycombinations.map {|x| print arraycombinations.join("") }
arraycombinations.map {|ar| ar.split(",") }
arraycombinations.each {|index| arraycombinations(index).join("") }
How can I isolate the join function to apply to only the second dimension within a multidimensional array?
Assuming you already have an array of arrays such as
a = [["1","2","3"],["1","3","2"],["2","1","3"],["2","3","1"], ["3","1","2"],["3","2","1"]]
a.map { |i| i.join}
#=>["123", "132", "213", "231", "312", "321"]
It's simple really
"123".split("").permutation.to_a.map { |x| x.join }
Let me explain a bit:
"123".split("") gives you an array ["1","2","3"]
permutation.to_a gives you array of arrays [["1","2","3"], ["2","1","3"] ... ]
then you must join each of those arrays inside with map { |x| x.join }
and you get the required end result.
Like this:
arraycombinations.map(&:join)
# => ["123", "132", "213", "231", "312", "321"]

Ruby Nested Array

I have a nested array that looks like this:
#nested = [
['1','2','3'],
['1','5','9'],
['1','4','7'],
['3','5','7'],
['3','6','9'],
['7','8','9'],
['4','5','6'],
['2','5','8']
]
I'd like to take a user input of any integer (that 1..9) and find every array that has that input integer.
Not sure how to do it.
Use select:
num_to_search = "9"
#nested.select do |array|
array.include? num_to_search
end
#=> [["1", "5", "9"], ["3", "6", "9"], ["7", "8", "9"]]

Why doesn't array.sort work on an array created from a string in Ruby?

If I manually create an array with
array = ["2","1","3"]
array.sort will return a sorted version of the array.
But if I create an array by using
array2 = ["213".split(//)]
or
array2 = []
array2 << "213".split(//)
array2.sort will return the unsorted array.
Why doesn't this work? Are the arrays created like that somehow different, and if yes, how?
The expression "213".split(//) already returns an array, so in both cases you're actually creating an array and adding that entire thing as the first element in a new array.
["213".split(//)]
=> [["2", "1", "3"]]
array2 << "213".split(//)
=> [["2", "1", "3"]]
Notice the double brackets. Sorting this array has no effect, because it contains just one element (itself an array). You want to remove the surrounding brackets:
"213".split(//)
=> ["2", "1", "3"]
You define array2 as ["213".split(//)]. This puts an array (["2","1","3"]) inside an array. The output is:
array = ["213".split(//)]
=> [["2", "1", "3"]]
When you try and sort that, it sorts the "bigger" array: the one with one element!
This works, though:
array = "213".split(//)
=> ["2", "1", "3"]
array.sort
=> ["1", "2", "3"]
This bit of code:
array2 = ["213".split(//)]
is creating an array with one element that is an array. To sort the array that you are interested in:
array2[0].sort
But, more likely, you should remove the outer brackets to avoid creating the array of arrays. The same applies to this:
array2 = []
array2 << "213".split(//)
It creates an empty array and then adds an array element which happens to be another array.
In the first case, you're creating an array whose elements are "2", "1", and "3". In the second case, you're creating an array that contains an array whose elements are "2", "1", and "3":
ruby-1.8.7-p334 :003 > array = ["2","1","3"]
=> ["2", "1", "3"] # Notice that this is an array of three elements
ruby-1.8.7-p334 :001 > array2 = ["213".split(//)]
=> [["2", "1", "3"]] #...but this is an array of an array of three elements
ruby-1.8.7-p334 :005 > array2 = "213".split(//)
=> ["2", "1", "3"] # Remove those extra brackets and it's equivalent to the first case.
If you're read this far, you probably figured out that in the second case, you were just sorting a single-element array and seeing that single element, which was unchanged.

Resources