Related
I am trying to get the differences between two arrays of equal length in ruby.
array1 = [1,2,3,4]
array2 = [1,2,4,5]
diffArray = []
diffArray = array1 - array2 # => [3]
But I would like to save the index at which the differences occur relative to array1 or array2. For example, I would like diffArray to be saved as
#diffArray => [nil,nil,3,nil] or #diffArray => ["","",3,""]
Is there a way to do this in ruby?
Your question is not clear.
If you are interested in the values of array1 at the positions at which the values do not match, you might want to use this:
array1 = [1,2,3,4]
array2 = [1,2,4,5]
array1.zip(array2).map { |a, b| a if a != b }
#=> [nil, nil, 3, 4]
If you are interested in the indexes at which the values do not match (hint: indexes start counting at 0), try this:
array1.zip(array2).map.with_index { |(a, b), i| i if a != b }
#=> [nil, nil, 2, 3]
It's not clear what the OP wants, but if the requirement is an array containing an index, rather than the elements of one of the arrays, then we want:
array1 = [1,2,3,4]
array2 = [1,2,4,5]
(0...array1.size).map { |index| index if array1[index] != array2[index] }
#=> [nil, nil, 2, 3]
I got this array:
array = [["R.M", 20], ["R.U-CS", 3], ["R.M-TIC", 3], ["R.J.CONF", 20]]
I want to sum the numeric values, so I have converted this into a single array by using flatten:
array = array.flatten
#=> ["R.M", 20, "R.U-CS", 3, "R.M-TIC", 3, "R.J.CONF", 20]
then:
a = []
array.each do |r|
a << r if r.class == Fixnum
end
a
This works, but I am searching for a simpler solution, maybe converting the array into an "only numeric" array first:
[20, 3, 3, 20]
Not that obvious, but you can pass a class or module to grep to select its instances:
array.grep(Integer)
#=> [20, 3, 3, 20]
or
array.grep(Numeric)
#=> [20, 3, 3, 20]
or to grep_v in order to exclude its instances:
array.grep_v(String)
#=> [20, 3, 3, 20]
This works because grep uses === for pattern matching. If you pass a class, it invokes Module#=== (each class is a module) which returns true if the object is an instance of the receiver:
Integer === 20
#=> true
Integer === 'foo'
#=> false
However, your actual problem can be solved much easier. Given this array:
array = [["R.M", 20], ["R.U-CS", 3], ["R.M-TIC", 3], ["R.J.CONF", 20]]
And assuming that the second value is always a number, you can use sum with a block:
array.sum { |string, number| number }
#=> 46
or to sum each sub-array's last value: (which is the same in this case)
array.sum(&:last)
#=> 46
One might use class’ case-equality Module#=== to determine a class of elements and to reject strings:
array = ["R.M", 20, "R.U-CS", 3, "R.M-TIC", 3, "R.J.CONF", 20]
array.reject &String.method(:===)
#⇒ [
# [0] 20,
# [1] 3,
# [2] 3,
# [3] 20
# ]
array.delete_if { |x| x.kind_of(String) }
array.map { |item| item if item.is_a? Integer }.compact
# [20, 3, 3, 20]
More compact:
array.select { |item| item.is_a? Integer }
Or even shorter:
array.select &Integer.method(:===)
Follow the below code
array.reject { |c| c.class.name.eql?('String') }
With is_a?
array.reject { |c| c.is_a?(String) }
If your arrays are formatted like your example then this will work:
array.select.with_index { |_,i| i.odd? } #=> [20, 3, 3, 20]
use select to select only integers from the array
array = ["R.M", 20, "R.U-CS", 3, "R.M-TIC", 3, "R.J.CONF", 20]
array.select { |tmp| tmp.is_a?(Integer) }
#=> [20, 3, 3, 20]
use select! if you want to permanently change the content of your variable array with the integer-only content
I am trying to see if an array contains each element of another array. Plus I want to account for the duplicates. For example:
array = [1, 2, 3, 3, "abc", "de", "f"]
array contains [1, 2, 3, 3] but does not contain [2, 2, "abc"] - too many 2's
I have tried the below but obviously doesn't take into account the dupes.
other_arrays.each { |i| array.include? i }
This method iterates once over both arrays.
For each array, it creates a hash with the number of occurences of each element.
It then checks that for every unique element in subset, there are at least as many elements in superset.
class Array
def count_by
each_with_object(Hash.new(0)) { |e, h| h[e] += 1 }
end
def subset_of?(superset)
superset_counts = superset.count_by
count_by.all? { |k, count| superset_counts[k] >= count }
end
end
[1, 2, 3, 3, "abc", "de", "f"].count_by
#=> {1=>1, 2=>1, 3=>2, "abc"=>1, "de"=>1, "f"=>1}
[1, 2, 3, 3].count_by
#=> {1=>1, 2=>1, 3=>2}
[1, 2, 3, 3].subset_of? [1, 2, 3, 3, "abc", "de", "f"]
#=> true
[2, 2, "abc"].subset_of? [1, 2, 3, 3, "abc", "de", "f"]
#=> false
If you don't want to patch the Array class, you could define :
count_by(array) and subset_of?(array1, array2).
You could first create a useful instance method for the class Array:
class Array
def difference(other)
h = other.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 }
reject { |e| h[e] > 0 && h[e] -= 1 }
end
end
Then all elements of an array a are contained in array b if the following method returns true.
def subarray?(a,b)
a.difference(b).empty?
end
For example,
subarray? [1,2,3], [1,4,"cat",3,2]
#=> true
subarray? [1,2,3], [1,4,"cat",3,5]
#=> false
I've found Array#difference has such wide application that I proposed that it be added to the Ruby core. Details about the method and its uses can be found at the link and also in my answer to this SO question.
I'd like to split an array into sub arrays of a specified length.
I know that .each_slice will chunk an array into equal length subarrays with the remainder leftover like so:
a = [1,2,3,4,5,6,7,8,9,10]
a.each_slice(3).to_a => [[1,2,3],[4,5,6],[7,8,9],[10]]
However, say I want the output like this:
=> [[1],[2,3],[4,5,6],[7,8,9,10]]
Is there a method in ruby for slicing an array into different specified lengths depending on the arguments you give it?
Try this
a = [1,2,3,4,5,6,7,8,9,10]
slices = [1,2,3,4].map { |n| a.shift(n) }
This slices the array into pieces
NB, this mutates the original array.
I cannot see how to improve on #akuhn's answer, but here are a couple of other methods that could be used.
a = [1,2,3,4,5,6,7,8,9,10,11]
slice_sizes = [1,2,3,4]
#1 Stab out slices
def variable_slice(a, slice_sizes)
last = 0
slice_sizes.each_with_object([]) do |n,arr|
arr << a[last,n]
last += n
end
end
variable_slice(a, slice_sizes)
#=> [[1], [2, 3], [4, 5, 6], [7, 8, 9, 10]]
#2 Use recursion
def variable_slice(a, slice_sizes)
return [] if slice_sizes.empty?
i, *rest = slice_sizes
[a.first(i)].concat variable_slice(a[i..-1], rest)
end
variable_slice(a, slice_sizes)
#=> [[1], [2, 3], [4, 5, 6], [7, 8, 9, 10]]
Original Question: The original question was how to iterate to the next element in nested loop ruby. The answer taught an idiomatic approach to solve my problem and it needed a different question, so that when a user lands up here on googling, finds the correct thing
I have this requirement where there are two arrays, and each of them have unique values, in sorted order.
array1 = [2,3,4,5,6,7] #can not have repeated values[2,2,3]
array2 = [2,5,7]
I want to match elements of both arrays and print match found whenever a match is found, along with the index of both the arrays. This is the code which works fine.
array1.each do |arr1|
array2.each do |arr2|
if (arr1==arr2)
puts ("Match found element #{arr1} #{array1.index(arr1)} #{array2.index(arr2)}")
#some code here to move to the next element in array 1 and array 2 and continue looping from there
end
end
end
But this does not use the data structure's quality of being unique and sorted in any way. For instance, when in the above example, element 2 in array1 matches element 2 in array2, the element 2 in array2 should not continue trying to match other elements of array1 and also array1 should be moved to the next. I know there is something called next. But I think that just returns the next element and does not move the iterator to the next element? Also I have to move to the next of both the arrays. How do I go about doing that?
If you want to find matching elements between two arrays just use & like so:
array1 = [2,3,4,5,6,7] #does not have repeated values[2,2,3] and the values are sorted
array2 = [2,5,7]
matches = array1 & array2
=> [2, 5, 7]
To print the matches and indexes found in each array, just loop through the matches array like so:
matches.each do |number|
puts "Match found element #{number}"
puts "array1 index=#{array1.rindex(number)}"
puts "array2 index=#{array2.rindex(number)}"
end
Match found element 2
array1 index=0
array2 index=0
Match found element 5
array1 index=3
array2 index=1
Match found element 7
array1 index=5
array2 index=2
For your codes, you just need to use break to exit the array2 loop, move to the next element in array1.
array2.each_with_index do |arr1, i1|
array1.each_with_index do |arr2, i2|
if (arr1==arr2)
puts ("Match found element array1: #{arr1} #{i1} and array2: #{arr2} #{i2}")
#if match element is found, exit from the current loop
break
end
end
end
Match found element array1: 2 0 and array2: 2 0
Match found element array1: 5 1 and array2: 5 3
Match found element array1: 7 2 and array2: 7 5
or
(array1 & array2).map{ |e| puts "Match found element #{e}" }
Match found element 2
Match found element 5
Match found element 7
Here's another way:
array1 = [2,3,4,5,6,7]
array2 = [2,5,7]
h = array1.each_with_index.to_h
#=> {2=>0, 3=>1, 4=>2, 5=>3, 6=>4, 7=>5}
array2.each_with_index.with_object({}) { |(v,i),g| g[v] = [h[v],i] if h.key?(v) }
#=> {2=>[0, 0], 5=>[3, 1], 7=>[5, 2]}
The second expression can be broken down as follows:
e0 = array2.each_with_index
#=> #<Enumerator: [2, 5, 7]:each_with_index>
We can see the elements of this enumerator by converting it to an array:
e0.to_a
#=> [[2, 0], [5, 1], [7, 2]]
e1 = e0.with_object({})
#=> #<Enumerator: #<Enumerator: [2, 5,7]:each_with_index>:with_object({})>
e1.to_a
#=> [[[2, 0], {}], [[5, 1], {}], [[7, 2], {}]]
The elements of the enumerator e are passed to the block and assigned to the block variables by Enumerator#each. The first (obtained using Enumerator#next) is:
(v,i),g = e1.next
#=> [[2, 0], {}]
v #=> 2
i #=> 0
g #=> {}
We can now execute the block:
g[v] = [h[v],i] if h.key?(v)
#=> g[2] = [h[2],0] if h.key(v)
# g[2] = [0,0] if true
The next element of e1 is now passed to the block:
(v,i),g = e1.next
#=> [[5, 1], {}]
v #=> 5
i #=> 1
g #=> {2=>[0, 0]}
g[v] = [h[v],i] if h.key?(v)
# g[5] = [h[5], 1] if h.key?(5)
# g[5] = [3, 1] if true
g #=> {2=>[0, 0], 5=>[3, 1]}
The calculation for the last element of e1 is similar.