How to compare two tables using Ruby - ruby-on-rails

I have two arrays like this: [[word],[number of occurence] , ... ]
Table 1 : [[["web"], 9], [["paris"], 8], [["html5"], 6], [["css3"], 6] ... ]
Table 2 : [[["web"], 2], [["paris"], 3], [["word"], 5], [["class"], 6] ... ]
I want to compare table 2 with table 1 and only show words NOT on table 2.
With the example I would have get:
Table 2 doesn't have html5, css3
Does Ruby have a gem that can do that?

The structure used here is quite irregular, but remapping it to something easier to work with isn't hard:
def hashify(list)
list.map do |(word), count|
[ word, count ]
end.to_h
end
The |(word), count| declaration pulls word out of the nested array, it simplifies the code.
Given sample data it works like this:
table1 = [[["web"], 9], [["paris"], 8], [["html5"], 6], [["css3"], 6] ]
table2 = [[["web"], 2], [["paris"], 3], [["word"], 5], [["class"], 6] ]
hashify(table1)
# => {"web"=>9, "paris"=>8, "html5"=>6, "css3"=>6}
Then you can use this to compute the difference:
hashify(table1).keys - hashify(table2).keys
# => ["html5", "css3"]

No need for any gem. Array difference works just fine, but you need to extract the interesting words first :
table1 = [[["web"], 9], [["paris"], 8], [["html5"], 6], [["css3"], 6]]
table2 = [[["web"], 2], [["paris"], 3], [["word"], 5], [["class"], 6]]
def extract_words(table)
table.map{|sub_array| sub_array.flatten.first }
end
puts extract_words(table1) - extract_words(table2)
# html5
# css3
With hashes, it would be easier :
hash1 = {"web"=>9, "paris"=>8, "html5"=>6, "css3"=>6}
hash2 = {"web"=>2, "paris"=>3, "word"=>5, "class"=>6}
puts hash1.keys - hash2.keys
# html5
# css3

I assume that you don't need the numerical data, so i would propose a one-line solution to your problem :
(table1.flatten - table2.flatten).reject {|elem| elem.is_a?(Integer)}
That returns the following array :
=> ["html5", "css3"]

table1 = [[["web"], 9], [["paris"], 8], [["html5"], 6], [["css3"], 6] ]
table2 = [[["web"], 2], [["paris"], 3], [["word"], 5], [["class"], 6] ]
table1.flat_map(&:first) - table2.flat_map(&:first)
# => ["html5", "css3"]

Related

How to swap variables inside of a nested array?

I have 2 arrays that I've zipped together and now I'm trying to swipe values at even positions.
So this is what I've tried:
a = [1, 2, 3, 4]
b = [111, 222, 333, 444]
c = a.zip(b)
# Now c is equal to: [[1, 111], [2, 222],[3, 333],[4, 444]]
c.map.with_index do |item, index|
a = item[0]
b = item[1]
if index%2 == 0
a, b = b, a
end
end
What I would like to have:
c = [[1, 111], [222,2], [3, 333],[444, 4]]
But it's still not working, is there a better solution ? Or how could I fix mine to make it work ?
EDIT:
I've realized that I could probably just use the ".reverse" method to swap the element. But I still can't manage to make it work.
Perhaps try:
c.map.with_index do |item, index|
index%2 != 0 ? item.reverse : item
end
=> [[1, 111], [222, 2], [3, 333], [444, 4]]
I would probably go with
a = [1, 2, 3, 4]
b = [111, 222, 333, 444]
a.zip(b).each_with_index do |item, idx|
item.reverse! if idx.odd?
end
#=>[[1, 111], [222, 2], [3, 333], [444, 4]]
zip as you did and reverse! just the items where the index is odd.
Other options include:
a.map.with_index(1) do |item,idx|
[item].insert(idx % 2, b[idx -1])
end
#=>[[1, 111], [222, 2], [3, 333], [444, 4]]
Here we use with_index starting with 1 and then use the modulo method to determine if the item in b should be placed at index 0 or index 1.
Or
a.zip(b).tap {|c| c.each_slice(2) {|_,b| b.reverse!}}
#=>[[1, 111], [222, 2], [3, 333], [444, 4]]
Here we zip a and b as your example did then we take the sub Arrays in groups of 2 and reverse the second Array using reverse! which will modify the Array in place.

How to compare arrays inside an array with each other in ruby?

[ 1, 1, 3, 5 ] & [ 1, 2, 3 ] #=> [ 1, 3 ]
[ 'a', 'b', 'b', 'z' ] & [ 'a', 'b', 'c' ] #=> [ 'a', 'b' ]
I need the intersection of each array with all other arrays within an array.
So the array could look like ->
a = [[1, 2, 3], [3, 4, 5], [4, 5, 6]]
The result should look like ->
a = [[3],[3,4,5][4,5]]
Any suggestions?
Look into the combination method.
a = [[1, 2, 3], [3, 4, 5], [4, 5, 6],[1,"a","b"]]
p a.combination(2).map{|x,y| x & y } #=> [[3], [], [1], [4, 5], [], []]
And if you do not want the empty arrays in there:
p a.combination(2).map{|x,y| x & y }.reject(&:empty?) #=> [[3], [1], [4, 5]]
Edit: After seeing some examples what OP actually want here is how I would achieve the desired result:
original = [[1, 2, 3], [3, 4, 5], [4, 5, 6]]
def intersect_with_rest(array)
array.size.times.map do
first, *rest = array
array.rotate!
first & rest.flatten
end
end
p intersect_with_rest(original) #=> [[3], [3, 4, 5], [4, 5]]
p original #=> [[1, 2, 3], [3, 4, 5], [4, 5, 6]]
Or:
original = [[1, 2, 3], [3, 4, 5], [4, 5, 6]]
result = original.map.with_index do |x,i|
x & (original[0...i]+original[1+i..-1]).flatten
end
p result #=> [[3], [3, 4, 5], [4, 5]]
Yeah, finally I found a solution. Maybe there is a simpler way, but that works for me now..
c = [[1,2,3],[3,4,5],[4,5,6]]
results = [];c.length.times.each {|e| results.push c.rotate(e).combination(2).map {|x, y| x & y}}
results.map{|x, y| y + x}
=> [[3], [3, 4, 5], [4, 5]]
Thanks to #hirolau for the hint. Best regards

Best possible way to get two arrays merged

temp1 = [1,2,3,4]
temp2 = [4,3,2,1]
Two array merged with following output
temp3 = [ [1,4], [2,3], [3,2], [4,1] ]
It's not that temp array will have only 4 elements.
It can have 5-5, 6-6 elements not 5-6 in both array.
This is exactly the functionality provided by the zip method:
>> temp1.zip(temp2)
=> [[1, 4], [2, 3], [3, 2], [4, 1]]

How can I sort this array of arrays by the second value in each array?

I have the following array:
[[1, 2], [44, 1], [18395, 3]]
Which I obtained by using this code:
current_user.friends_products.where("units.primary_image_id IS NOT NULL").group_by{|u| u.creator_id}.map {|k,v| [k, v.length]}
I want to sort the array by the second value of each array from greatest to least. So, this is what I'm trying to achieve:
[[18395, 3], [1, 2], [44, 1]]
Use #sort_by with the second element descending:
x = [[1, 2], [44, 1], [18395, 3]]
x.sort_by { |a, b| -b }
#=> [[18395, 3], [1, 2], [44, 1]]
You can use this Array#sort block:
[[1, 2], [44, 1], [18395, 3]].sort { |a, b| b[1] <=> a[1] }
# => [[18395, 3], [1, 2], [44, 1]]
[[1, 2], [44, 1], [18395, 3]].sort_by(&:last).reverse
arr =[[1, 2], [44, 1], [18395, 3]]
arr.sort_by{|x,y|y}.reverse
# => [[18395, 3], [1, 2], [44, 1]]

How do I sort two arrays the same way?

I have two arrays:
a = [6, 4, 3]
b = [1, 3, 4]
I call a.sort:
a.sort = [3, 4, 6]
How do I sort array b so the values have the same position to values in array a before the sort?
It would be now:
b = [4, 3, 1]
So that values in b have the same position to values in array a.
You could combine both the arrays into one using the zip method. Once you combine a and b, you would get,
[[6, 1], [4, 3], [3, 4]]
Now sort the arrays, which would sort them based on the first element of each sub-array resulting in,
[[3, 4], [4, 3], [6, 1]]
Now we want to do the reverse of zip to get the first and second elements of each sub-array into a new array. Using transpose, we can get it back in the original form as,
[[3, 4, 6], [4, 3, 1]]
Thankfully using parallel assignment all of this is possible in one line. Here's the full code,
x, y = a.zip(b).sort.transpose
Now x should contain [3, 4, 6], and y should contain [4, 3, 1].
a = [6, 4, 3]
b = [1, 3, 4]
ra, rb = a.zip(b).sort_by(&:first).transpose
# ra => [3, 4, 6]
# rb => [4, 3, 1]
I don't know what you're trying to achieve, and I'm sure others could come up with a more elegant solution, but I would use a Hash instead. Assign the values of a a as the key, and the values of b as the values. You could iterate over a to accomplish this, or just in advance when you're creating this data. The result should be:
$ hash
=> {6 => 1, 4 => 3, 3 => 4}
a0 b0 a1 b1 a2 b2
$ hash.sort
=> [[3, 4], [4, 3], [6, 1]]
Like I said, not super smooth, but I've got turkey hangover...
[a, b].transpose.sort { |x, y| x[0] <=> y[0] }.transpose[1]
=> [4, 3, 1]
or
a, b = [a, b].transpose.sort { |x, y| x[0] <=> y[0] }.transpose

Resources