How to find matching values in ruby array? - ruby

Have arrays
[1, 2, 5] and [1, 2, 3]
I would like to extract matching values, if there is a method like:
[1, 2, 5].match([1, 2, 3]) #=> [1, 2]
Is there any method on array, thank you

Very simple:
[1,2,5] & [1,2,3] #=> [1,2]
Other useful array operations include:
[1,2,3] | [1,3,4] #=> [1,2,3,4]
[1,2,3] - [1,3,4] #=> [2]
[1,2,3] + [1,3,4] #=> [1,2,3,1,3,4]

Related

How can I modify an array without destroying an array assigned to it?

Consider the following:
array1 = [1, 2, 3, 4]
array2 = array1 # => [1, 2, 3, 4]
array2.pop
array2 # => [1, 2, 3]
array1 # => [1, 2, 3]
Why is array1 destroyed when I've only called pop on array2? Is there a way to pop the last value from array2 and leave array1 intact so that I get array1 # => [1, 2, 3, 4]?
It's an aliasing issue. Your references point to the same Array object in memory. If your arrays contain simple Integers like those dup method do the trick.
array2 = array1.dup
array2 = array1.dup
array2 = array1.clone => Your changes effects both arrays
I prefer Object#dup method, but here is one more option FYI:
> array1 = [1, 2, 3, 4]
#=> [1, 2, 3, 4]
> array2 = Array.new + array1
#=> [1, 2, 3, 4]
> array1.object_id
#=> 87422520
> array2.object_id
#=> 87400390

How can I get the next n number of elements using a Ruby enumerator?

I am trying to get the next n number of elements using a Ruby enumerator, with this:
a = [1, 2, 3, 4, 5, 6]
enum = a.each
enum.next(2) # expecting [1, 2]
enum.next(2) # expecting [3, 4]
But #next does not support that. Is there another way that I can do that?
Or shall I do?
What is the correct Ruby way to do that?
You can use take method
enum.take(2)
If you need slices of two elements, you could do:
e = enum.each_slice(2)
p e.next
#=> [1, 2]
p e.next
#=> [3, 4]
a = [1, 2, 3, 4, 5, 6]
enum = a.dup
enum.shift(2) # => [1, 2]
enum.shift(2) # => [3, 4]

How to search within a two-dimensional array

I'm trying to learn how to search within a two-dimensional array; for example:
array = [[1,1], [1,2], [1,3], [2,1], [2,4], [2,5]]
I want to know how to search within the array for the arrays that are of the form [1, y] and then show what the other y numbers are: [1, 2, 3].
If anyone can help me understand how to search only with numbers (as a lot of the examples I found include strings or hashes) and even where to look for the right resources even, that would be helpful.
Ruby allows you to look into an element by using parentheses in the block argument. select and map only assign a single block argument, but you can look into the element:
array.select{|(x, y)| x == 1}
# => [[1, 1], [1, 2], [1, 3]]
array.select{|(x, y)| x == 1}.map{|(x, y)| y}
# => [1, 2, 3]
You can omit the parentheses that correspond to the entire expression between |...|:
array.select{|x, y| x == 1}
# => [[1, 1], [1, 2], [1, 3]]
array.select{|x, y| x == 1}.map{|x, y| y}
# => [1, 2, 3]
As a coding style, it is a custom to mark unused variables as _:
array.select{|x, _| x == 1}
# => [[1, 1], [1, 2], [1, 3]]
array.select{|x, _| x == 1}.map{|_, y| y}
# => [1, 2, 3]
You can use Array#select and Array#map methods:
array = [[1,1], [1,2], [1,3], [2,1], [2,4], [2,5]]
#=> [[1, 1], [1, 2], [1, 3], [2, 1], [2, 4], [2, 5]]
array.select { |el| el[0] == 1 }
#=> [[1, 1], [1, 2], [1, 3]]
array.select { |el| el[0] == 1 }.map {|el| el[1] }
#=> [1, 2, 3]
For more methods on arrays explore docs.
If you first select and then map you can use the grep function to to it all in one function:
p array.grep ->x{x[0]==1}, &:last #=> [1,2,3]
Another way of doing the same thing is to use Array#map together with Array#compact. This has the benefit of only requiring one block and a trivial operation, which makes it a bit easier to comprehend.
array.map { |a, b| a if b == 1 }
#=> [1, 2, 3, nil, nil, nil]
array.map { |a, b| a if b == 1 }.compact
#=> [1, 2, 3]
You can use each_with_object:
array.each_with_object([]) { |(x, y), a| a << y if x == 1 }
#=> [1, 2, 3]

Ruby enumerator chaining

In this example,
[1, 2, 3].each_with_index.map{|i, j| i * j}
# => [0, 2, 6]
my understanding is that, since each_with_index enumerator is chained to map, map behaves like each_with_index by passing an index inside the block, and returns a new array.
For this,
[1, 2, 3].map.each_with_index{|i, j| i * j}
# => [0, 2, 6]
I'm not sure how to I interpret it.
In this example,
[1, 2, 3, 4].map.find {|i| i == 2}
# => 2
I was expecting the the output to be [2], assuming that map is chained to find, and map would return a new array.
Also, I see this:
[1, 2, 3, 4].find.each_with_object([]){|i, j| j.push(i)}
# => [1]
[1, 2, 3, 4].each_with_object([]).find{|i, j| i == 3}
# => [3, []]
Can you let me know how to interpret and understand enumerator chains in Ruby?
You might find it useful to break these expressions down and use IRB or PRY to see what Ruby is doing. Let's start with:
[1,2,3].each_with_index.map { |i,j| i*j }
Let
enum1 = [1,2,3].each_with_index
#=> #<Enumerator: [1, 2, 3]:each_with_index>
We can use Enumerable#to_a (or Enumerable#entries) to convert enum1 to an array to see what it will be passing to the next enumerator (or to a block if it had one):
enum1.to_a
#=> [[1, 0], [2, 1], [3, 2]]
No surprise there. But enum1 does not have a block. Instead we are sending it the method Enumerable#map:
enum2 = enum1.map
#=> #<Enumerator: #<Enumerator: [1, 2, 3]:each_with_index>:map>
You might think of this as a sort of "compound" enumerator. This enumerator does have a block, so converting it to an array will confirm that it will pass the same elements into the block as enum1 would have:
enum2.to_a
#=> [[1, 0], [2, 1], [3, 2]]
We see that the array [1,0] is the first element enum2 passes into the block. "Disambiguation" is applied to this array to assign the block variables the values:
i => 1
j => 0
That is, Ruby is setting:
i,j = [1,0]
We now can invoke enum2 by sending it the method each with the block:
enum2.each { |i,j| i*j }
#=> [0, 2, 6]
Next consider:
[1,2,3].map.each_with_index { |i,j| i*j }
We have:
enum3 = [1,2,3].map
#=> #<Enumerator: [1, 2, 3]:map>
enum3.to_a
#=> [1, 2, 3]
enum4 = enum3.each_with_index
#=> #<Enumerator: #<Enumerator: [1, 2, 3]:map>:each_with_index>
enum4.to_a
#=> [[1, 0], [2, 1], [3, 2]]
enum4.each { |i,j| i*j }
#=> [0, 2, 6]
Since enum2 and enum4 pass the same elements into the block, we see this is just two ways of doing the same thing.
Here's a third equivalent chain:
[1,2,3].map.with_index { |i,j| i*j }
We have:
enum3 = [1,2,3].map
#=> #<Enumerator: [1, 2, 3]:map>
enum3.to_a
#=> [1, 2, 3]
enum5 = enum3.with_index
#=> #<Enumerator: #<Enumerator: [1, 2, 3]:map>:with_index>
enum5.to_a
#=> [[1, 0], [2, 1], [3, 2]]
enum5.each { |i,j| i*j }
#=> [0, 2, 6]
To take this one step further, suppose we had:
[1,2,3].select.with_index.with_object({}) { |(i,j),h| ... }
We have:
enum6 = [1,2,3].select
#=> #<Enumerator: [1, 2, 3]:select>
enum6.to_a
#=> [1, 2, 3]
enum7 = enum6.with_index
#=> #<Enumerator: #<Enumerator: [1, 2, 3]:select>:with_index>
enum7.to_a
#=> [[1, 0], [2, 1], [3, 2]]
enum8 = enum7.with_object({})
#=> #<Enumerator: #<Enumerator: #<Enumerator: [1, 2, 3]:
# select>:with_index>:with_object({})>
enum8.to_a
#=> [[[1, 0], {}], [[2, 1], {}], [[3, 2], {}]]
The first element enum8 passes into the block is the array:
(i,j),h = [[1, 0], {}]
Disambiguation is then applied to assign values to the block variables:
i => 1
j => 0
h => {}
Note that enum8 shows an empty hash being passed in each of the three elements of enum8.to_a, but of course that's only because Ruby doesn't know what the hash will look like after the first element is passed in.
Methods you are mentioning are defined on Enumerable objects. These methods behave differently depending on whether you pass a block or not.
When you do not pass a block, they typically return an Enumerator object, to which you can chain further methods like each_with_index, with_index, map, etc.
When you pass a block to these methods, they return different kinds of object depending on what will make sense for that particular method.
For methods like find, its purpose is to find the first object that satisfies a condition, and it does not make particular sense to wrap that in an array, so it returns that object bare.
For methods like select or reject, their purpose is to return all relevant objects, so they cannot return a single object, and they have to be wrapped in an array (even when the relevant object happens to be a single object).

Find all subsets in an array

I need help with solving this ruby array question.
Get all the subsets of an array. Unique set only. No repeats of any number. num_subset([1,2,3]) ==> result should be [[], ["1"], ["1", "2"], ["1", "2", "3"], ["1", "3"], ["2"], ["2", "3"], ["3"]]
def num_subset(arr)
holder =[]
order_subset = [[]]
(arr.length).times do |m|
arr.map do |n|
holder += [n]
order_subset << holder
end
holder =[] # resets holder
arr.shift # takes the first element out
end
order_subset
end
My result ==> [[], ["1"], ["1", "2"], ["1", "2", "3"], ["2"], ["2", "3"], ["3"]. My problem is that I am missing one result ["1", "3"]
Need some help pointing me to the right direction. Spent hours on this already. Do not use #combination short cut. I need to work this out manually.
a = [1, 2, 3]
arr = []
for i in 0..(a.length) do
arr = arr + a.combination(i).to_a
end
> arr
# [[], [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]]
I believe this is the most rubyish solution to find combinations
a = [1,2,3]
p (0..a.length).collect { |i|
a.combination(i).to_a
}.flatten(1)
# [[], [1], [2], [3], [4], [1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4], [1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4], [1, 2, 3, 4]]
Looks like you're looking at a starting point somewhere in the array and then looking at all sub arrays from that starting point on, after which you move the starting point down. That way, you're missing the sub arrays with gaps. For [1,2,3], the only sub array with a gap is [1,3].
For example (ignoring [] since you've hardcoded that)
[(1),2,3,4] -> [1]
[(1,2),3,4] -> [1,2]
[(1,2,3),4] -> [1,2,3]
[(1,2,3,4)] -> [1,2,3,4]
[1,(2),3,4] -> [2]
[1,(2,3),4] -> [2,3]
[1,(2,3,4)] -> [2,3,4]
[1,2,(3),4] -> [3]
[1,2,(3,4)] -> [3,4]
[1,2,3,(4)] -> [4]
So I'd expect your output for [1,2,3,4] to be [[],[1],[1,2],[1,2,3],[1,2,3,4],[2],[2,3],[2,3,4],[3],[3,4],[4]].
You really need to rethink your algorithm. You could try recursion. Take the head of your array (1), construct all possible sub arrays of the tail ([2,3]), duplicate that, and prefix half of it with the head. Of course, to construct the sub arrays, you call the same function, all the way down to an empty array.
[1,2,3] ->
....[2,3] ->
........[3] ->
............[] ->
................# an empty array is its own answer
................[]
............# duplicating the empty array and prefixing one with 3
............[3], []
........# duplicating the result from the last step and prefixing half with 2
........[2,3], [2], [3], []
....# duplicating the result from the last step and prefixing half with 1
....[1,2,3], [1,2], [1,3], [1], [2,3], [2], [3], []
I have created a method to find all subsets of an array. I am using binary number to make iteration of array very less.
def find_subset(input_array)
no_of_subsets = 2**input_array.length - 1
all_subsets = []
expected_length_of_binary_no = input_array.length
for i in 1..(no_of_subsets) do
binary_string = i.to_s(2)
binary_string = binary_string.rjust(expected_length_of_binary_no, '0')
binary_array = binary_string.split('')
subset = []
binary_array.each_with_index do |bin, index|
if bin.to_i == 1
subset.push(input_array[index])
end
end
all_subsets.push(subset)
end
all_subsets
end
Output of [1,2,3] would be
[[3], [2], [2, 3], [1], [1, 3], [1, 2], [1, 2, 3]]
My solution.
The basic idea over here is that subsets of an array are
Subsets of the array with one less element - let's call these old subsets
array of elements containing that one less element added each of the old subsets
For Example -
Subsets([1, 2, 3]) are -
Subsets([1, 2]) - old_subsets
Tack on 3 to each of old_subsets
def subsets(arr)
return [[]] if arr.empty?
old_subsets = subsets(arr.drop(1))
new_subsets = []
old_subsets.each do |subset|
new_subsets << subset + [arr.first]
end
old_subsets + new_subsets
end
Recursive solution
def subsets(arr)
(l = arr.pop) ? subsets(arr).map{|s| [s,s+[l]]}.flatten(1) : [[]]
end
or in a more descriptive way
def subsets(arr)
return [[]] if arr.empty?
last = arr.pop
subsets(arr).map{|set| [set, set + [last]]}.flatten(1)
end

Resources