ruby return array of values that exist in every nested array - arrays

I want to build a ruby function that finds the in-common elements of ALL nested arrays. For instance, given the following arrays, this is the answer it would come up with...
[[1, 2, 3], [1, 2], [2]]
=> [2]
[[1, 2, 3], [1, 2, 4]]
=> [1, 2]
I could do,
arr = [[1, 2, 3], [1, 2], [2]]
arr[0] & arr[1] & arr[2]
=> [2]
But not sure how to make that operation dynamic.

You're looking for the Enumerable#reduce (aka #inject) method. This performs aggregate calculations over an enumerable like an array. If you don't pass it an initial value, it will use the first element of the collection as the initial value.
arr.reduce { |a, b| a & b } # => [2]
And there's a convenient shorthand for this type of expression:
[[1, 2, 3], [1, 2], [2]].reduce(:&) # => [2]
[[1, 2, 3], [1, 2, 4]].reduce(:&) # => [1, 2]
[[1], [2]].reduce(:&) # => []
[].reduce(:&) # => nil

Related

Turn nested hash to two dimensional array in Ruby

I want to write a method that can receive a nested hash and return a nested array of two dimensional arrays.
hash_to_a({1=>2, 2=>3, {3=>4, 5=>6}=>7}) # [[1, 2], [2, 3], [[[3, 4], [5, 6]], 7]]
hash_to_a({{5=>{1=>3, 2=>4}}=>{7=>8}}) # [[[[5, [[1, 3], [2, 4]]]], [[7, 8]]]]
hash_to_a({5=>{1=>3, 2=>4}}) # [[5, [[1, 3], [2, 4]]]]
So far i've got this:
def hash_to_a(a_hash)
result = []
a_hash.each { |k, v|
if k.is_a?(Hash)
result << k.to_a
else
result << k
end
if v.is_a?(Hash)
result << v.to_a
else
result << v
end
}
result
end
The results are of course not desirable
hash_to_a({1=>2, 2=>3, {3=>4, 5=>6}=>7}) # [1, 2, 2, 3, [[3, 4], [5, 6]], 7]
hash_to_a({{5=>{1=>3, 2=>4}}=>{7=>8}}) # [[[5, {1=>3, 2=>4}]], [[7, 8]]]
hash_to_a({5=>{1=>3, 2=>4}}) # [5, [[1, 3], [2, 4]]]
How can I accomplish the wanted results? I also tried recusion but just can't wrap my mind around this problem.
Start with your simplest case, with no nesting.
def hash_to_a(hash) # { 3 => 4 }
hash.map do |k, v| # 3, 4
[k, v] # [3, 4]
end
end
Now the problem is that for the more complicated cases we don't want to return the key or value if it's a hash, we want to convert it from a hash first.
def hash_to_a(hash)
hash.map do |k, v|
[hash_to_a(k), hash_to_a(v)]
end
end
This will blow up with our simple case because 3 does not have a method #map. This is because we're not handling the base case yet. Just takes a line of code to keep from trying to map things that aren't hashes. We want it to behave just like our first try: do nothing but return the key or the value.
def hash_to_a(object)
return object unless object.is_a? Hash
object.map do |k, v|
[hash_to_a(k), hash_to_a(v)]
end
end
And we're done.
hash_to_a({1=>2, 2=>3, {3=>4, 5=>6}=>7}) # [[1, 2], [2, 3], [[[3, 4], [5, 6]], 7]]
hash_to_a({{5=>{1=>3, 2=>4}}=>{7=>8}}) # [[[[5, [[1, 3], [2, 4]]]], [[7, 8]]]]
hash_to_a({5=>{1=>3, 2=>4}}) # [[5, [[1, 3], [2, 4]]]]
You can use recursion
def h_to_a(h)
h.map { |k,v| [k.is_a?(Hash) ? h_to_a(k) : k, v.is_a?(Hash) ? h_to_a(v) : v] }
end
h_to_a({ 1=>2, 2=>3, { 3=>4, 5=>6 }=>7 })
#=> [[1, 2], [2, 3], [[[3, 4], [5, 6]], 7]]
h_to_a({ { 5=>{ 1=>3, 2=>4 } }=>{ 7=>8 } })
#=> [[[[5, [[1, 3], [2, 4]]]], [[7, 8]]]]
h_to_a({ 5=>{ 1=>3, 2=>4 } })
#=> [[5, [[1, 3], [2, 4]]]]
This obviously works with any level of nesting.
What about this one:
def deep_to_array(hash)
return hash unless hash.is_a?(Hash)
array = hash.to_a
array.each_with_index do |(k,v), index|
array[index][0] = deep_to_array(k)
array[index][1] = deep_to_array(v)
end
array
end
Or a concise one:
def deep_to_array2(hash)
return hash unless hash.is_a?(Hash)
hash.map do |k,v|
[deep_to_array2(k), deep_to_array2(v)]
end
end
Example:
deep_to_array(({1=>2, 2=>3, {3=>4, 5=>6}=>7}))
=> [[1, 2], [2, 3], [[[3, 4], [5, 6]], 7]]
Yet another variation (using builtin Hash#to_a)
As class method:
class Hash
def flatten_to_a
to_a.map {|i| i.is_a?(Array) ? i.map {|i| i.is_a?(Hash) ? i.flatten_to_a : i} : i}
end
end
As standalone method:
def f2a hash
return hash unless Hash === hash
hash.to_a.map {|i| i.is_a?(Array) ? i.map {|i| i.is_a?(Hash) ? i.flatten_to_a : i} : i}
end

Best way to replace nils from array with elements of another array

I have two arrays:
a = [nil, 1, nil]
b = [4, 5, 6]
And i want to replace nil elements from first array with related elements from second array:
[4, 1, 6]
What is the best way to do it?
You can use zip and the || operator to do it:
result = a.zip(b).map{ |x,y| x || y }
If you want to replace exactly nil, but not false elements:
a.map.with_index { |e, i| e.nil? ? b[i] : e }
# => [4, 1, 6]
You can use
a.zip(b).map(&:compact).map(&:first) #=> [4, 1, 6]
Steps:
a.zip(b)
#=> [[nil, 4], [1, 5], [nil, 6]]
a.zip(b).map(&:compact)
#=> [[4], [1, 5], [6]]
a.zip(b).map(&:compact).map(&:first)
#=> [4, 1, 6]
By virtue of Array#compact this approach removes nil elements only from the zipped pairs i.e. false elements are not removed.
Another way is by using a block when creating this new array like so:
a = [nil, 1, nil]
b = [4, 5, 6]
Array.new(a.size) { |i| a[i].nil? ? b[i] : a[i] }
#=> [4, 1, 6]
Still another variant:
a.zip(b).map{|a, b| [*a, *b].first}
# => [4, 1, 6]
This distinguishes nil from false, as expected. But note that you cannot use this with elements that are broken by to_a, such as hashes.

Removing the smaller sets of values from an array of arrays in Swift

Given an array that consists of arrays containing integers.
[[2], [3], [2, 2], [5], [7], [2, 2, 2], [3, 3]]
What would be the preferred way in Swift to remove the arrays that contains a smaller number of elements with a certain value and keep only the larger arrays containing that value.
The result from the input above would be
[[5], [7], [2, 2, 2], [3, 3]]
Use a [Int: [Int]] dictionary to keep track of the largest array for the value specified by the key.
let arrays = [[2], [3], [2, 2], [5], [7], [2, 2, 2], [3, 3]]
var largest = [Int: [Int]]()
for arr in arrays {
// Get the first value from the array
if let first = arr.first {
// current is the count for that key already in dictionary largest
// If the key isn't found, the nil coalescing operator ?? will
// return the default count of 0.
let current = largest[first]?.count ?? 0
// If our new array has a larger count, put it in the dictionary
if arr.count > current {
largest[first] = arr
}
}
}
// Convert the dictionary's values to an array for the final answer.
let result = Array(largest.values)
print(result) // [[5], [7], [2, 2, 2], [3, 3]]
This same logic can be used with reduce to provide the result in one line:
let result = arrays.reduce([Int: [Int]]()) { var d = $0; guard let f = $1.first else { return d }; d[f] = d[f]?.count > $1.count ? d[f] : $1; return d }.map { $1 }
Alternate Version
This version uses a [Int: Int] dictionary to just keep the counts of the largest array found for each key, and then reconstructs the arrays at the end using an array constructor.
let arrays = [[2], [3], [2, 2], [5], [7], [2, 2, 2], [3, 3]]
var counts = [Int: Int]()
for arr in arrays {
if let first = arr.first {
counts[first] = max(counts[first] ?? 0, arr.count)
}
}
let result = counts.map { [Int](count: $1, repeatedValue: $0) }
print(result) // [[5], [7], [2, 2, 2], [3, 3]]
This same logic can be used with reduce to provide the result in one line:
let result = arrays.reduce([Int: Int]()) { var d = $0; guard let f = $1.first else { return d }; d[f] = max(d[f] ?? 0, $1.count); return d }.map { [Int](count: $1, repeatedValue: $0) }
I was just about to write up my answer when I saw that vacawama had responded with something very similar. Decided to come back to it though just because it's an interesting problem to play around with. So my alternative is almost certainly much slower that vacawama's solutions and doesn't preserve the order, but I thought it was interesting as an example of the alternatives that you have for solving problems like this in Swift.
var items = [[2], [3], [2, 2], [5], [7], [2, 2, 2], [3, 3]]
let reduced = items.sort({
let lhs = $0.first, rhs = $1.first
return lhs == rhs ? $0.count > $1.count : lhs < rhs
}).reduce( [[Int]]()) { (res, items) in
return res.last?.last != items.last ? res + [items] : res
}
print(reduced) // [[2, 2, 2], [3, 3], [5], [7]]
Or if you'd rather cram all that on a single line:
var items = [[2], [3], [2, 2], [5], [7], [2, 2, 2], [3, 3]]
let reduced = items.sort({ let lhs = $0.first, rhs = $1.first; return lhs == rhs ? $0.count > $1.count : lhs < rhs }).reduce([[Int]]()) { $0.last?.last != $1.last ? $0 + [$1] : $0 }
print(reduced) // [[2, 2, 2], [3, 3], [5], [7]]
Just an alternative using forEach:
let arrays = [[2], [2, 2], [5], [7], [2, 2, 2], [3, 3], [3]]
var largest: [Int: [Int]] = [:]
arrays.forEach({
guard let first = $0.first else { return }
largest[first] = [Int](count: max($0.count,largest[first]?.count ?? 0), repeatedValue: first)
})
Array(largest.values) // [[5], [7], [2, 2, 2], [3, 3]]

Delete from Array and return deleted elements in Ruby

How can I delete some elements from an array and select them?
For example:
class Foo
def initialize
#a = [1,2,3,4,5,6,7,8,9]
end
def get_a
return #a
end
end
foo = Foo.new
b = foo.get_a.sth{ |e| e < 4 }
p b # => [1,2,3]
p foo.get_a # => [4,5,6,7,8,9,10]
What I can use instead of foo.get_a.sth?
If you don't need to retain the object id of a:
a = [1,2,3,4,5,6,7,8,9,10]
b, a = a.partition{|e| e < 4}
b # => [1, 2, 3]
a # => [4, 5, 6, 7, 8, 9, 10]
If you do need to retain the object id of a, then use a temporal array c:
a = [1,2,3,4,5,6,7,8,9,10]
b, c = a.partition{|e| e < 4}
a.replace(c)
Rails 6 now has this:
a = [1, 2, 3]
#=> [1, 2, 3]
a.extract! { |n| n.even? }
#=> [2]
a
#=> [1, 3]
If you were only deleting one item, this doesn't require duplicating the array, etc:
array = [{ id: 1 }, { id: 2 }, {id: 3 }]
array.delete_at(array.find_index { |element| element[:id] == 1 })
#=> {:id=>1}
a = [1, 2, 3, 4]
a.dup - (a.delete_if(&:even?))
#=> [2, 4]
a
#=> [1, 3]
a = [1, 2, 3, 4]
b = a.dup - (a.delete_if { |e| e < 4 })
a
#=> [4]
b
#=> [1, 2, 3]
Edit: It sounds like you are just after #select...
a = [1, 2, 3, 4]
a.select { |e| e < 3 }
#=> [1, 2]
I still don't believe ruby doesn't have something for this in its default libraries. There should be #drop method equivalent for this. Surely there's a gem available that would add the functionality to the array class. But who needs gems when you can just break out your own scripts:
#in initializer somewhere
class Array
def exclude(obj)
x = self
x.delete(obj)
x
end
end
I may submit a pull request to the ruby github project for this method. This superman-patch works very well so far.

How to pop an element from array without affecting other variables?

When I try to pop an element from an array, it pops. When I assign that array to another variable before popping and then if I pop, the pop operation affects both the arrays.
For example:
ruby-1.9.2-p290 :339 > a= [1,2,3]
=> [1, 2, 3]
ruby-1.9.2-p290 :340 > b = a
=> [1, 2, 3]
ruby-1.9.2-p290 :341 > a
=> [1, 2, 3]
ruby-1.9.2-p290 :342 > b
=> [1, 2, 3]
ruby-1.9.2-p290 :343 > a.pop
=> 3
ruby-1.9.2-p290 :344 > a
=> [1, 2]
ruby-1.9.2-p290 :345 > b
=> [1, 2] #WHY?
ruby-1.9.2-p290 :346 > x = [1,2,3]
=> [1, 2, 3]
ruby-1.9.2-p290 :347 > y = x
=> [1, 2, 3]
ruby-1.9.2-p290 :348 > z = x
=> [1, 2, 3]
ruby-1.9.2-p290 :349 > y
=> [1, 2, 3]
ruby-1.9.2-p290 :350 > z
=> [1, 2, 3]
ruby-1.9.2-p290 :351 > y.pop
=> 3
ruby-1.9.2-p290 :352 > y
=> [1, 2]
ruby-1.9.2-p290 :353 > z
=> [1, 2] # WHY?
ruby-1.9.2-p290 :354 > x
=> [1, 2]
ruby-1.9.2-p290 :355 >
If I use pop, all the variables are affected. How do I keep the original array and pop from the other one only?
If you assign an array to a new variable, it's not copied, but you only set a reference to the original array. If you want to keep the original array, you have to clone it by using dup:
ruby-1.9.2-p180 :001 > a = [1,2,3]
=> [1, 2, 3]
ruby-1.9.2-p180 :002 > b = a.dup
=> [1, 2, 3]
ruby-1.9.2-p180 :003 > b.pop
=> 3
ruby-1.9.2-p180 :004 > a
=> [1, 2, 3]
ruby-1.9.2-p180 :005 > b
=> [1, 2]
The assignment operator in ruby is making a copy of the value only if it deal with POD (Plain Old Data) objects like numbers, strings. In other cases, it simply copies the reference to the object.
And don't forget, that dup (and clone) method makes only shallow copy of object. It means that if your array have other non-POD objects inside, they won't be copied.
inner = [1,2,3]
=> [1, 2, 3]
outer = [inner, 7,8,9]
=> [[1, 2, 3], 7, 8, 9]
outer_dup = outer.dup
inner.pop
=> 3
outer
=> [[1, 2], 7, 8, 9]
outer_dup
=> [[1, 2], 7, 8, 9]
You can avoid it by overriding clone method to handle making deep copy by yourself.
Ruby is very object oriented. With very few exceptions, Ruby almost always passes variables by reference, not value. It's a pretty important concept. Check this out. It isn't ruby, but it might help demystify it a bit. Hope that helps!

Resources