Efficient way to subtract arrays and get index of resulting subarray - ruby-on-rails

Lets say I have the following arrays:
arr1 = [
['a', 'b'],
['c', 'd'],
['e', 'f']
]
arr2 = [
['g', 'h'],
['i', 'k'],
['a', 'b']
]
I want to find the elements in arr1 that do not exist in arr2 and the index of the elements in arr1
I can do this with the following but this isn't very efficient and is not a pretty solution. There can also be many elements in the arrays so this does not scale. Is there a better way to do this?
diff = arr1 - arr2
diff_with_index = diff.map { |x| { index: arr1.index(x), values: x } }
print diff_with_index
# [{:index=>1, :values=>["c", "d"]}, {:index=>2, :values=>["e", "f"]}]

When you have to do multiple include? checks, the most efficient way is to turn one of the lists into a set or hash beforehand, so you can have O(1) lookup time, so something like this:
require 'set'
arr2_set = Set.new(arr2)
arr1.each_index.select { |idx| !arr2_set.include?(arr1[idx]) }

Here is one way.
i1 = (0..arr1.size-1).to_a
#=> [0, 1, 2]
h1 = arr1.zip(i1).to_h
#=> {["a", "b"]=>0, ["c", "d"]=>1, ["e", "f"]=>2}
i1 - arr2.map { |a| h1[a] }
#=> [1, 2]
Note that
arr2.map { |a| h1[a] }
#=> [nil, nil, 0]

Related

Remove array elements at indices non destructively

Let's say you have something like this:
my_array = ['some_rather', 'long_named', 'array_element', 'entry']
I want to remove arbitrary entries by index from my_array without changing it and I want the filtered (i.e. array with indices removed) to be returned from my call. Furthermore, I want to avoid chaining 4 separate calls and write a block doing so.
Example:
filtered_array = my_array.drop_indices(1,3)
You could chain Enumerable's with_index onto Array's reject method to do what you want, though this might violate your desire to not chain separate method calls or write a block to do this:
my_array = ['some_rather', 'long_named', 'array_element', 'entry', 'long_named']
indices_to_remove = [1, 3]
filtered = my_array.reject.with_index { |_, index| indices_to_remove.include?(index) }
p filtered # => ["some_rather", "array_element", "long_named"]
p my_array # => ["some_rather", "long_named", "array_element", "entry", "long_named"]
If this isn't acceptable, the only other thing I can think of right now, to keep duplicate items (as noted in my comment to your solution), is to change from indices_to_remove to indices_to_keep:
my_array = ['some_rather', 'long_named', 'array_element', 'entry', 'long_named']
indices_to_remove = [1, 3]
indices_to_keep = [*(0...my_array.length)] - indices_to_remove
filtered = my_array.values_at(*indices_to_keep)
p filtered # => ["some_rather", "array_element", "long_named"]
p my_array # => ["some_rather", "long_named", "array_element", "entry", "long_named"]
For Arrays with duplicate Elements the best I know is:
array = [0,15,8,15,8]
indices_to_remove = [1,4]
res = array.reject.with_index{ |_,i| indices_to_remove.include?(i) }
returns
[0,8,15]
Additionally for arrays with unique entries such as a set of users
(using variable definitions from the question)
filtered_array = my_array - my_array.values_at(1,3)
Bonus, if your indices are inside an array themselves:
indices_to_remove = [1,3]
filtered_array = my_array - my_array.values_at(*indices_to_remove)
I think this is rather descriptive, not awkward and not shorter than needed.
One more possible solution with some addition, now it's also will work with negative indexes:
array = %w( a b c d e f )
indexes = [1, 2, -1, -9, -6, 6]
def array_except(array, *indexes)
indexes = indexes.map { |e| e.negative? ? e + array.length : e }
array.values_at(*((0...array.length).to_a - indexes))
end
array_except(array, *indexes)
=> ["d", "e"]
array_except(array, 0, -1)
=> ["b", "c", "d", "e"]

How to clone array of hashes and add key value using each loop

I want to clone an array of hashes and then to clone it into more than one.
irb(main):001:0> arr = [{a: "one", b: "two"}, {a: "uno", b: "due"}, {a: "en", b: "to"}]
=> [{:a=>"one", :b=>"two"}, {:a=>"uno", :b=>"due"}, {:a=>"en", :b=>"to"}]
irb(main):002:0> arr_1 = arr.clone
=> [{:a=>"one", :b=>"two"}, {:a=>"uno", :b=>"due"}, {:a=>"en", :b=>"to"}]
irb(main):003:0> arr_2 = arr.clone
=> [{:a=>"one", :b=>"two"}, {:a=>"uno", :b=>"due"}, {:a=>"en", :b=>"to"}]
Dynamically I want to add id into the hashes.
irb(main):004:0> arr_1.each { |k| k[:id] = 1 }
=> [{:a=>"one", :b=>"two", :id=>1}, {:a=>"uno", :b=>"due", :id=>1}, {:a=>"en", :b=>"to", :id=>1}]
irb(main):005:0> arr_2.each { |k| k[:id] = 2 }
=> [{:a=>"one", :b=>"two", :id=>2}, {:a=>"uno", :b=>"due", :id=>2}, {:a=>"en", :b=>"to", :id=>2}]
But the result of arr_1's id is affected by arr_2 each loop operation which is become 2
irb(main):006:0> arr_1
=> [{:a=>"one", :b=>"two", :id=>2}, {:a=>"uno", :b=>"due", :id=>2}, {:a=>"en", :b=>"to", :id=>2}]
I have tried by using
arr_1 = arr
arr_2 = arr
but the result keeps showing the same result.
How to make the arr_1 hashes :id = 1 and arr_2 hashes :id = 2 ?
Let's see what is happening.
arr = [{a: "cat", b: "dog"}, {a: "uno", b: "due"}]
arr.object_id
#=> 4557280
arr1 = arr
arr1.object_id
#=> 4557280
As you see, the variables arr and arr1 hold the same object, because the objects have the same object id.1 Therefore, if that object is modified, arr and arr1 will still both hold that object. Let's try it.
arr[0] = {a: "cat", b: "dog"}
arr
#=> [{:a=>"cat", :b=>"dog"}, {:a=>"uno", :b=>"due"}]
arr.object_id
#=> 4557280
arr1
#=> [{:a=>"cat", :b=>"dog"}, {:a=>"uno", :b=>"due"}]
arr1.object_id
#=> 4557280
If we want to be able to modify arr in this way without it affecting arr1, we use the method Kernel#dup.
arr
#=> [{:a=>"cat", :b=>"dog"}, {:a=>"uno", :b=>"due"}]
arr1 = arr.dup
#=> [{:a=>"cat", :b=>"dog"}, {:a=>"uno", :b=>"due"}]
arr.object_id
#=> 4557280
arr1.object_id
#=> 3693480
arr.map(&:object_id)
#=> [2631980, 4557300]
arr1.map(&:object_id)
#=> [2631980, 4557300]
As you see, arr and arr1 now hold different objects. Those objects, however, are arrays whose corresponding elements (hashes) are the same objects. Let's modify one of arr's elements.
arr[1][:a] = "owl"
arr
#=> [{:a=>"cat", :b=>"dog"}, {:a=>"owl", :b=>"due"}]
arr.map(&:object_id)
#=> [2631980, 4557300]
arr still contains the same objects, but we have modified one. Let's look at arr1.
arr1
#=> [{:a=>"cat", :b=>"dog"}, {:a=>"owl", :b=>"due"}]
arr1.map(&:object_id)
#=> [2631980, 4557300]
Should we be surprised that arr1 has changed as well?
We need to dup both arr and the elements of arr.
arr = [{a: "one", b: "two"}, {a: "uno", b: "due"}]
arr1 = arr.dup.map(&:dup)
#=> [{:a=>"one", :b=>"two"}, {:a=>"uno", :b=>"due"}]
arr.object_id
#=> 4149120
arr1.object_id
#=> 4182360
arr.map(&:object_id)
#=> [4149200, 4149140]
arr1.map(&:object_id)
#=> [4182340, 4182280]
Now arr and arr1 are different objects and they contain different (hash) objects, so any change to one will not affect the other. (Try it.)
Now suppose arr were as follows.
arr = [{a: "cat", b: [1,2]}]
Let's make the copy.
arr1 = arr.dup.map(&:dup)
#=> [{:a=>"cat", :b=>[1, 2]}]
Now modify arr[0][:b].
arr[0][:b] << 3
#=> [{:a=>"cat", :b=>[1, 2, 3]}]
arr1
#=> [{:a=>"cat", :b=>[1, 2, 3]}]
Drat! arr1 changed. We can again look at object ids to see why that happened.
arr.object_id
#=> 4488500
arr1.object_id
#=> 4503140
arr.map(&:object_id)
#=> [4488520]
arr1.map(&:object_id)
#=> [4503100]
arr[0][:b].object_id
#=> 4488560
arr1[0][:b].object_id
#=> 4488560
We see that arr and arr1 are different objects and there respective hashes are the same elements, but the array is the same object for both hashes. We therefore need to do something like this:
arr1[0][:b] = arr[0][:b].dup
but that's still not good enough if arr were:
arr = [{a: "cat", b: [1,[2,3]]}]
What we need is a method that will make a deep copy. A common solution for that is to use the methods Marshal::dump and Marshal::load.
arr = [{a: "cat", b: [1,2]}]
str = Marshal.dump(arr)
#=> "\x04\b[\x06{\a:\x06aI\"\bcat\x06:\x06ET:\x06b[\ai\x06i\a"
arr1 = Marshal.load(str)
#=> [{:a=>"cat", :b=>[1, 2]}]
arr[0][:b] << 3
#=> [{:a=>"cat", :b=>[1, 2, 3]}]
arr
#=> [{:a=>"cat", :b=>[1, 2, 3]}]
arr1
#=> [{:a=>"cat", :b=>[1, 2]}]
Note we could write:
arr1 = Marshal.load(Marshal.dump(arr))
As explained in the doc, the serialization used by the Marshal methods is not necessarily the same for different Ruby versions. If, for example, dump were used to produce a string that was saved to file and later load was invoked on the contents of the file, using a different version of Ruby, the contents may not be readable. Of course that's not a problem in this application of the methods.
1. To make it easier to see differences in object id's I've only shown the last seven digits. They in all cases are preceded by the digits 4877798.

ruby uniq on the first two columns of two dimensional array

I have a two dimensional array with three fields in the second dimension.
Is it possible to use uniq on the first two fields of the second dimension?
I have seen array.uniq! {|c| c.first}. When I am correct, this apply uniq on the first field of the array.
Is it possible to use something like array.uniq! {|c| c.first c.second}?
#array = Array.new()
#array << Array.new([journal.from_account_number, journal.from_account,
journal.buchungsart])
There are several entries in #array.
The question was how to get unique values from array not considering journal.buchungsart.
The answer was: #array = #array.uniq! {|c| [c.first, c.second]}
Yes, put the values in an array:
array.uniq! {|c| [c.first, c.second]}
Just return an array of two elements in the block.
Since Array#second is not defined in the standard library, do
array.uniq! { |c| [c[0], c[1]] }
instead, which can be further simplified to array.uniq! { |c| c[0..1] }
You could also make use of the fact that hash keys are unique.
arr = [[1, 2, 3],
[2, 1, 4],
[1, 2, 5]]
a = arr.each_with_object({}) { |row, h| h.update(row.first(2)=>row) }.values
#=> [[1, 2, 5], [2, 1, 4]]
See Hash#update (aka merge!).
Before extracting the hash values with Hash#value, we have computed the following hash.
arr.each_with_object({}) { |row, h| h.update(row.first(2)=>row) }
#=> {[1, 2]=>[1, 2, 5], [2, 1]=>[2, 1, 4]}
Notice that, for given values of the first two elements in a row, it is the last row of arr with those values that is to be "kept". The first such row in arr is to be kept, use the following.
arr.each_with_object({}) { |row, h| h.update(row.first(2)=>row) { |_,o,_| o } }.values
#=> [[1, 2, 3], [2, 1, 4]]
This does not mutate arr. If arr is to be modified, write
arr.replace(a)
where a is defined above.
Array#first accepts a parameter :
%w(a b c d e f).first(2)
# => ["a", "b"]
so you could just use :
array.uniq!{ |c| c.first(2) }

How to expand elements of an array into sub-arrays?

I have a huge array
huge = 1000
huge_array = (1..huge).to_a
How to best "expand" this array so that each element becomes a sub-array of format [original_element, "default value"], preferably in a memory-friendly way (without an explicit #map loop?)
expanded_huge_array = huge_array.some_magic
#=> [[1, "default value"],[2, "default value"], ... [1000, "default value"]]
huge_array.zip(['default value'] * huge_array.size)
BTW, you might simulate this behaviour with Hash with default:
arr = Hash.new { |h, key| huge_array.include?(key) ? [key, 'default value'] : nil }
arr[1]
#⇒ [1, 'default value']
arr[10000]
#⇒ nil
Try Array#product:
Returns an array of all combinations of elements from all arrays.
>> [1,2,3].product(["a"])
=> [[1, "a"], [2, "a"], [3, "a"]]

How do you double number in ruby for an iterator

Need to double each value in my array. I know double is not a command, but not sure what else to use.
odds = [1,3,5,7,9]
array.each do |x|
x += double
print "#{x}"
end
Use Array#map to create a new array
odds = [1,3,5,7,9]
arr = odds.map{|x| x*2}
arr.inspect
# => [2,6,10,14,18]
To modify the same array use Array#map!
odds = [1,3,5,7,9]
odds.map!{|x| x*2}
odds.inspect
# => [2,6,10,14,18]
Do you mean 'double' as in multiply by 2, or double as in duplicate?
array = [1,3,5,7,9]
array.each { |x| print "#{x*2}"; }
But you probably want either a new array, or to map your existing array,
result = []
result = array.map { |x| x*2 }
#or
result = array.map! { |x| x*2 }
Here is an example of duplicate,
result = []
array.map { |x| result << x; result << x; } #duplicate
see here: http://www.natontesting.com/2011/01/01/rubys-each-select-and-reject-methods/
Doing the Simplest Thing Possible
The simplest thing to do is to simply iterate over your array with Array#map and Kernel#p. For example:
odds = [1, 3, 5, 7, 9]
odds.map { |i| p i*2 }
Defining a Custom Method
If you need more control, you can create a custom method that handles a block or returns an enumerator. For example:
def double *array
array.flatten!
block_given? ? array.map { |i| yield i*2 } : array.map { |i| i*2 }.to_enum
end
enumerator = double 1, 2, 3
#=> #<Enumerator: ...>
enumerator.each { |i| p i }
#=> [2, 4, 6]
double(1, 2, 3) { |i| p i }
#=> [2, 4, 6]
This is probably overkill for your use case, but it's a useful technique to know if you want to work with enumerators and blocks. Hope it helps!
The way that I have found to do it is to times / equal it by 2. For example:
odds = [1,3,5,7,9]
odds.each do |x|
x *= 2
print x
end

Resources