In my ruby project, I have an array of arrays of arrays which is the result of doing some heavy graph traversal
example:
[[], [1, 2, 3, 4, 5], [[], [], [[], [], [34, 54, 23, 45]]]]
I want the final product to be
[[1, 2, 3, 4, 5], [34, 54, 23, 45]]
flatten will leave me with
[1, 2, 3, 4, 5, 34, 54, 23, 45]
so I don't want that
I'm stuck and could use some help on how to do this
so far, I have
class Array
def flatten_blanks
each{ |elem| elem.flatten_blanks if elem.is_a?(Array) }
reject!{ |elem| elem.is_a?(Array) && elem.length < 1 }
self
end
end
which is close but leaves me with something like
[[[[[[[1, 2, 3, 4, 5]]]]]]], [[[[[[[34, 54, 23, 45]]]]]]]
squeeze = -> (arr, acc = []) do
case arr.compact.first
when NilClass then acc
when Array
arr.reject(&:empty?).map { |e| squeeze.(e, acc) }.first
else acc << arr
end
end
squeeze.(arr)
#⇒ [[1, 2, 3, 4, 5], [34, 54, 23, 45]]
For you example it does the trick:
array = [[], [1, 2, 3, 4, 5], [[], [], [[], [], [34, 54, 23, 45]]]]
array.map(&:flatten).delete_if(&:empty?)
It will produce:
[[1, 2, 3, 4, 5], [34, 54, 23, 45]]
require 'json'
arr = [[], [1, 2, 3, 4, 5], [[], [], [[], [], [34, 54, 23, 45]]]]
JSON.parse("[#{arr.to_json.scan(/\[[^\[\]]+\]/).join(',')}]")
#=> [[1, 2, 3, 4, 5], [34, 54, 23, 45]]
A recursive method (similar to mudsie's recursive proc) could also be used.
def recurse(arr)
arr.each_with_object([]) { |e,a|
e.first.is_a?(Array) ? a.concat(recurse e) : a << e unless e.empty? }
end
recurse(arr)
#=> [[1, 2, 3, 4, 5], [34, 54, 23, 45]]
Let's call your array arr
arr.map(&:flatten).reject(&:empty?)
To explain a little, you run flatten on the array's elements instead of the array itself, and then delete the empty subarrays.
Related
I'm working on a coding challenge to sort the odd elements of an array while keeping all even elements in their initial position.
I can sort the odd elements in groups between even elements while leaving the evens alone, but I want them to be ordered throughout the array. Here is my code:
def sort_array source_array
return [] if source_array.empty?
a = source_array
n = a.length
loop do
swapped = false
(n - 1).times do |i|
if (a[i] > a[i + 1]) && a[i].odd? && a[i + 1].odd?
a[i], a[i + 1] = a[i + 1], a[i]
swapped = true
end
end
break if not swapped
end
a
end
source_array = [7, 5, 35, 8, 12, 17, 47, 47, 37, 64, 22, 55, 13]
p sort_array(source_array)
# => [5, 7, 35, 8, 12, 17, 37, 47, 47, 64, 22, 13, 55]
You could try this approach:
Create an array with all odds (leaving source_array intact). Sort
this array.
Consider source_array element by element. Is the
element even then leave it . If it is odd, then replace it with the
first element on the sorted odds list. Remove the first element of the
sorted list.
Here are a couple of ways to do that.
arr = [7, 5, 35, 8, 12, 17, 47, 47, 37, 64, 22, 55, 13]
Both begin with the following calculation
odd, even = arr.each_with_index.partition { |n,i| n.odd? }
#=> [[[7, 0], [5, 1], [35, 2], [17, 5], [47, 6], [47, 7], [37, 8], [55, 11], [13, 12]],
# [[8, 3], [12, 4], [64, 9], [22, 10]]]
odd
#=> [[7, 0], [5, 1], [35, 2], [17, 5], [47, 6], [47, 7], [37, 8], [55, 11], [13, 12]]
even
#=> [[8, 3], [12, 4], [64, 9], [22, 10]]
See Enumerable#each_with_index, Enumerable#partition and Integer#odd?.
#1
odd_val, odd_idx = odd.transpose
#=> [[7, 5, 35, 17, 47, 47, 37, 55, 13],
# [0, 1, 2, 5, 6, 7, 8, 11, 12]]
a = even.concat(odd_val.sort.zip(odd_idx))
#=> [[8, 3], [12, 4], [64, 9], [22, 10], [5, 0], [7, 1], [13, 2],
# [17, 5], [35, 6], [37, 7], [47, 8], [47, 11], [55, 12]]
h = a.map(&:reverse).to_h
#=> {3=>8, 4=>12, 9=>64, 10=>22, 0=>5, 1=>7, 2=>13, 5=>17, 6=>35, 7=>37,
# 8=>47, 11=>47, 12=>55}
h.values_at(*0..arr.size-1)
#=> [5, 7, 13, 8, 12, 17, 35, 37, 47, 64, 22, 47, 55]
Note:
odd_val.sort.zip(odd_idx)
#=> [5, 7, 13, 17, 35, 37, 47, 47, 55].zip([0, 1, 2, 5, 6, 7, 8, 11, 12])
#=> [[5, 0], [7, 1], [13, 2], [17, 5], [35, 6], [37, 7], [47, 8], [47, 11], [55, 12]]
See Array#sort, Array#zip and Hash#values_at.
#2
a = odd.map(&:first).sort
#=> [5, 7, 13, 17, 35, 37, 47, 47, 55]
even.each { |n,i| a.insert(i,n) }
a #=> [5, 7, 13, 8, 12, 17, 35, 37, 47, 64, 22, 47, 55]
See Array#insert.
source_array = [7, 5, 35, 8, 12, 17, 47, 47, 37, 64, 22, 55, 13]
odd_sorted = source_array.select(&:odd?).sort
source_array.map {|input| input.even? ? input : odd_sorted.shift }
# [5, 7, 13, 8, 12, 17, 35, 37, 47, 64, 22, 47, 55]
I'm using Ruby 2.4. I'm having trouble creating an array of arrays. I have this
data_cols = [[], []]
lines.each do |line|
parts = [0, *shared_space_indexes, line.size].each_cons(2).map { |a, b| line[a...b].strip }
parts.each_with_index do |part, index|
data_cols[index][data_cols[index] ? data_cols[index].size : 0] = part
end
end
But I get a "NoMethodError: undefined method `[]=' for nil:NilClass" error on the "data_cols[index][data_cols[index] ? data_cols[index].size : 0] = part" line. What I'm trying to do is for each parts array, push each item in "parts" onto its own array correspoding to the index of that element in parts. So, for instance, if the first iteration of the loop has parts equal to
[1, 5, 8, 12]
I would have a data_cols array that looks like
[[1], [5], [8], [12]]
and if the next iteration of the loop had a parts array that looked like
[19, 20, 21, 22]
the data_cols array would then be changed to
[[1, 19], [5, 20], [8, 21], [12, 22]]
It's hard to test without knowing what lines and shared_space_indexes look like.
2 columns → a matrix
zip
For the second part of your question you can use :
[1, 5, 8, 12].zip([19, 20, 21, 22])
#=> [[1, 19], [5, 20], [8, 21], [12, 22]]
transpose
[[1, 5, 8, 12],[19, 20, 21, 22]].transpose
#=> [[1, 19], [5, 20], [8, 21], [12, 22]]
With transpose, both arrays need to have the same size.
3 columns → matrix
array1 = [1, 5, 8, 12]
array2 = [19, 20, 21, 22]
array3 = [99, 88, 77, 66]
p [array1, array2, array3].transpose
#=> [[1, 19, 99], [5, 20, 88], [8, 21, 77], [12, 22, 66]]
columns → rows
If you have an array of arrays :
arrays = [
[1, 5, 8, 12],
[19, 20, 21, 22],
[99, 88, 77, 66]
]
p arrays.transpose
#=> [[1, 19, 99], [5, 20, 88], [8, 21, 77], [12, 22, 66]]
So in your case, I'd just concentrate on creating an array of arrays with the same length, and use transpose at the end.
matrix + column → matrix
As you noticed, if you have
data_cols = [[1, 19], [5, 20], [8, 21], [12, 22]]
arr = [99, 88, 77, 66]
data_cols.zip(arr) would produce a weird half-nested array :
[[[1, 19], 99], [[5, 20], 88], [[8, 21], 77], [[12, 22], 66]]
You can use :
p data_cols.zip(arr).map(&:flatten)
#=> [[1, 19, 99], [5, 20, 88], [8, 21, 77], [12, 22, 66]]
I have created a dictionary like this
var MyArray: [String:[String:[Int]]] = [
"xx": ["x1": [1, 2, 3], "x2": [4, 5, 6], "x3": [7, 8, 9]],
"yy": ["y1": [10, 11, 12], "y2": [13, 14, 15], "y3": [16, 17, 18]]]
How can I change the value 3 in "x1" in "xx" to an other number?
I don't know that this is the number 3 but i know that it is in MyArray["xx"]!["x1"]![2]
// example setup
var myArray: [String:[String:[Int]]] = [
"xx": ["x1": [1, 2, 3], "x2": [4, 5, 6], "x3": [7, 8, 9]],
"yy": ["y1": [10, 11, 12], "y2": [13, 14, 15], "y3": [16, 17, 18]]]
// value to be replaced
let oldNum = 3
// value to replace old value by
let newNum = 4
// extract the current value (array) for inner key 'x1' (if it exists),
// and proceed if 'oldNum' is an element of this array
if var innerArr = myArray["xx"]?["x1"], let idx = innerArr.index(of: oldNum) {
// replace the 'oldNum' element with your new value in the copy of
// the inner array
innerArr[idx] = newNum
// replace the inner array with the new mutated array
myArray["xx"]?["x1"] = innerArr
}
print(myArray)
/* ["yy": ["y3": [16, 17, 18], "y2": [13, 14, 15], "y1": [10, 11, 12]],
"xx": ["x1": [1, 2, 4], "x3": [7, 8, 9], "x2": [4, 5, 6]]]
^ ok! */
Based on the following Q&A:
Dictionary in Swift with Mutable Array as value is performing very slow? How to optimize or construct properly?
A more performant approach would be actually removing the inner array (for key "x1"); mutating it; and re-adding it to the dictionary
// check if 'oldNum' is a member of the inner array, and if it is: remove
// the array and mutate it's 'oldNum' member to a new value, prior to
// adding the array again to the dictionary
if let idx = myArray["xx"]?["x1"]?.index(of: oldNum),
var innerArr = myArray["xx"]?.removeValue(forKey: "x1") {
innerArr[idx] = newNum
myArray["xx"]?["x1"] = innerArr
}
print(myArray)
// ["yy": ["y3": [16, 17, 18], "y2": [13, 14, 15], "y1": [10, 11, 12]], "xx": ["x1": [1, 2, 4], "x3": [7, 8, 9], "x2": [4, 5, 6]]]
If you know the index of the number you would like to change out of the three, you can change the number 3 directly using the subscripts ["xx"]?["x1"]?[2].
var myArray = [
"xx": [
"x1": [1, 2, 3],
"x2": [4, 5, 6],
"x3": [7, 8, 9]
],
"yy": [
"y1": [10, 11, 12],
"y2": [13, 14, 15],
"y3": [16, 17, 18]
]
]
array["xx"]?["x1"]?[2] = 4
a = [[5, 6], [6, 5], [7, 4], [1, 0, 0], [9, 9], [6, 8], [8, 6], [1, 8, 0], [9, 0]]
How can I sum the integers inside the inner array and return an array of sum of the inner array?
I need it to return [(5+6), (6+5), (7+4), (1+0+0), ...], thus [11, 11, 11, 1, 18, 14, 14, 9, 9]
a.map{|a| a.inject(:+)}
# => [11, 11, 11, 1, 18, 14, 14, 9, 9]
Try
sum_arry = a.map { |sub_arry| sub_arry.inject(&:+) }
Then sum_arry should be the array you want.
Use map + reduce
a.map { |a| a.reduce(:+) }
#board = {1=>0, 2=>0, 3=>0, 4=>0, 5=>0, 6=>0, 7=>0, 8=>"b", 9=>"r", 10=>"u", 11=>"c", 12=>0, 13=>0, 14=>"d", 15=>"h", 16=>"s", 17=>"l", 18=>0, 19=>0, 20=>"o", 21=>"i", 22=>"l", 23=>"b", 24=>0, 25=>0, 26=>"g", 27=>"t", 28=>"f", 29=>"e", 30=>0, 31=>0, 32=>0, 33=>0, 34=>0, 35=>0, 36=>0}
#words = {"shift"=>[16, 15, 21, 28, 27], "bell"=>[23, 29, 22, 17], "curb"=>[11, 10, 9, 8], "dog"=>[14, 20, 26]}
#col1 = [8, 14, 20, 26]
#col2 = [9, 15, 21, 27]
#col3 = [10, 16, 22, 28]
#col4 = [11, 17, 23, 29]
#rcol1 = Array.new
#rcol2 = Array.new
#rcol3 = Array.new
#rcol4 = Array.new
#cols = [#col1, #col2, #col3, #col4]
#rcols0 = [#rcol1, #rcol2, #rcol3, #rcol4]
out = #words["dog"]
out.each do |remove|
#board[remove] = 100
end
def colclone
nums = 0
#rcols0.each do |fixy|
a = #cols[nums]
a.each do |stick|
fixy[nums].push #board[stick]
end
nums += 1
end
end
Here's the actual code. I tried to simplify but I think I must have left something out.
I am working on a board game. What I want is
--> #rcol1 = [0, 1, 2, 3]
what I am getting is those results in my reference array instead
--> #rcols0 = [[0, 1, 2, 3], ...]
Any help would be greatly appreciated!
You have the wrong idea, or the wrong vocabulary, about how it works. names does not store the names of the arrays. names stores a reference to the Array objects. It doesn't matter what they're named.
array1 = [0, 1, 2]
array2 = [10,20,30]
names = [array1, array2]
# names[1] contains a reference to array2
# So this is the same as array2[2]
puts names[1][2] # 30
Your code works fine.
names.each do |update|
update.push 3
update.push 4
end
puts array1.inspect # [0, 1, 2, 3, 4]
Your solution seems to be working:
array1 = [1,2,3]
=> [1, 2, 3]
array2 = [4,5,6]
=> [4, 5, 6]
names = [array1, array2]
=> [[1, 2, 3], [4, 5, 6]]
names.each do |update|
update.push 8
update.push 9
end
=> [[1, 2, 3, 8, 9], [4, 5, 6, 8, 9]]
array1
=> [1, 2, 3, 8, 9]
array2
=> [4, 5, 6, 8, 9]
I think something like this is what you're looking for:
array1 = [0, 1, 2]
array2 = [10, 11, 12]
array3 = [20, 21, 22]
names = [array1, array2, array3]
names.each do |array|
i = array[array.length-1] + 1
array.push(i,i+1)
end
p array1
p array2
p array3
p names
Output for the above is:
[0, 1, 2, 3, 4] #array1
[10, 11, 12 ,13, 14] #array2
[20, 21, 22, 23, 24] #array3
[[0, 1, 2, 3, 4], [10, 11, 12, 13, 14], [20, 21, 22, 23, 24]] #names array
Modify and/or extend the code to suit your needs.