Comparing the order of three arrays with the same elements - ruby

I have three arrays. The arrays all have the same size and contain the same elements. However, the three arrays must not be in the same order. How do I verify that the elements are not in the same order?
Here's how I've implemented it:
all_elements_equal = true
array1.zip(array2, array3) do |first, second, third|
if first != second && second != third && first != third
all_elements_equal = false
end
end
If all_elements_equal is false, presumably the arrays are not in the same order. However, there is a possibility that this will not be the case if only one of the arrays is different and the other two are identical.
So, my question is, how do I make the test stronger, and is there a more elegant way of implementing the code? Full disclosure: I am new to Ruby.

Have you tried this?
array1 == array2 || array1 == array3 || array2 == array3

In general, if you have array arr of N such arrays, you can just check if there are any duplicates there:
arr.length == arr.uniq.length
because, for example:
[[1,2,3],[2,3,1],[1,2,3]].uniq
#=> [[1, 2, 3], [2, 3, 1]]
[[1,2,3],[2,3,1],[2,1,3]].uniq
#=> [[1, 2, 3], [2, 3, 1], [2, 1, 3]]

I don't know Ruby, but I think you need to inverse your logic.
anyElementEqual=false
do
if first==second || first==third || second==third
anyElementEqual=true
end
etc.

Related

How to remove duplicate contiguous elements in an array?

Given an array such as
arr = [1,1,1,2,2,3,4,1,2]
I wish to replace each contiguous string of equal elements with a single instance of that element. Here the result would be
[1,2,3,4,1,2]
The three 1s are replaced by a single 1 and the two 2's are replaced by a single 2.
How can I do that for an arbitrary array of Ruby objects?
I know you can do it with chunk_while and map, but there might be other ways:
[1,1,1,2,2,3,4,1,2].chunk_while { |e, f| e == f }.map(&:first)
# [1, 2, 3, 4, 1, 2]
With chunk_while you split the array by chunks whenever the block is evaluated to true, for that the block yields two variables, the "element before" and the "element after", that's to say, for every iteration you're going to get this:
[1, 1]
[1, 1]
[1, 2]
[2, 2]
[2, 3]
[3, 4]
[4, 1]
[1, 2]
After applying the logic in the proc, it'll chunk the receiver whenever the first and second yielded elements are equal, and you get:
[[1, 1, 1], [2, 2], [3], [4], [1], [2]]
After that you can map that result to get only one element from each array - there are many ways, but first is enough.
The chunk_while proc can also be shortened to (&:==), leaving as [1,1,1,2,2,3,4,1,2].chunk_while(&:==).map(&:first).
Similarly and just out of curiosity, you can use slice_when and last to save 2 characters: [1,1,1,2,2,3,4,1,2].slice_when(&:!=).map(&:last).
You can use recursive functions to solve this type problems.
I tried different tests. It works.
mylist = [1,1,1,2,2,3,4,1,2] #Output:[1,2,3,4,1,2]
mylist2 = [1,1,1,1,1,1,2,2,2,2,3,3,5,5,5,4,1,2] # Output:[1,2,3,5,4,1,2]
mylist3 = [-1,-1,-2,3,5,5,5,4,3,8,8,8,9,6,0,0,6,5] # Output: [-1,-2,3,5,4,3,8,9,6,0,6,5]
def myfunc(mylist)
mylist.each_with_index do |val, index|
if val == mylist[index+1]
mylist.delete_at(index+1)
myfunc(mylist) # here we call same function again
end
end
return mylist
end
print myfunc(mylist)
puts
print myfunc(mylist2)
puts
print myfunc(mylist3)
arr = [1,1,1,2,2,3,4,1,2]
arr.each do |i|
(0..arr.length()).each do |j|
if arr[j] == arr[j+1]
arr.delete_at(j)
break
end
end
end
print arr
What is happening here is the entire list is being traversed; matching consecutive elements in each iteration.
Everytime a match is found, delete it and move on to the next iteration (break).
In effect, you will be changing the length of the array in each iteration. hence the break to avoid index errors.
The nested looping is added to handle any no. of consecutive duplicates.

How to achieve all elements swapped in an array

I have an ordered array which contain 1 to 1000000 elements.
I want to achieve an array such that the elements in the array are swapped with its next element.For instance if we assume the array elements are
[1,2,3,4,5,6]
I want to return an array with elements as
[2,1,4,3,6,5]
How do I achieve this in ruby for 100000 such elements? Can anyone guide me?
a = [1,2,3,4,5,6]
a.each_slice(2).map{|inner_a| inner_a.reverse}.flatten
# => [2, 1, 4, 3, 6, 5]
Description:
a.each_slice(2)returns an enumerator (#<Enumerator: [1, 2, 3, 4, 5, 6]:each_slice(2)>) with two element couples from your array. To see try a.each_slice(2).to_a. This returns [[1, 2], [3, 4], [5, 6]] with I only have to flatten for your expected result.
See also the first comment if you prefer a shorter notation of it.
Assuming you want to use a minimum amount of memory (since you chose a large array), and assuming the result is to be a mutated array (i.e. not a new array, but a change to the existing array) and finally assuming a is always an even number of elements...
a.each_index{|x| a[x], a[x+1] = a[x+1], a[x] if x.even?}
Possibly more performant...
(0...a.size).step(2) {|x| a[x], a[x+1] = a[x+1], a[x]}
You can try this.
arr = (1..100000).to_a
arr.each_with_index.each_slice(2){|(_,i), (_,j)| arr[i], arr[j] = arr[j], arr[i]}

Ruby - pushing values from an array combination to a new array

I am trying to print all the different sums of all combinations in this array [1,2,3]. I want to first push every sum result to a new array b, then print them using b.uniq so that non of the sum results are repeated.
However, with the code I have, the 3 repeats itself, and I think it is because of the way it is pushed into the array b.
Is there a better way of doing this?
a = [1,2,3]
b = []
b.push a
b.push a.combination(2).collect {|a,b| (a+b)}
b.push a.combination(3).collect {|a,b,c| (a+b+c)}
puts b.uniq
p b #[[1, 2, 3], [3, 4, 5], [6]]
Can someone please help me with this? I am still new in ruby.
Because an Array of arbitrary length can be summed using inject(:+), we can create a more general solution by iterating over the range 1..n, where n is the length of the Array.
(1..(a.size)).flat_map do |n|
a.combination(n).map { |c| c.inject(&:+) }
end.uniq
#=> [1, 2, 3, 4, 5, 6]
By using flat_map, we can avoid getting the nested Array result, and can call uniq directly on it. Another option to ensure uniqueness would be to pass the result to a Set, for which Ruby guarantees uniqueness internally.
require "set"
sums = (1..(a.size)).flat_map do |n|
a.combination(n).map { |c| c.inject(&:+) }
end
Set.new(sums)
#=> #<Set: {1, 2, 3, 4, 5, 6}>
This will work for an any Array, as long as all elements are Fixnum.
If all you want is an array of the possible sums, flatten the array before getting the unique values.
puts b.flatten.uniq
What is happening is uniq is running over a multi-dimensional array. This causes it to look for duplicate arrays in your array. You'll need the array to be flattened first.

Ruby Lowest Integer in a multidimensional Array

Let's say that I have a multidimensional array with array's inside of it that each have two numbers. How do I iterate over the entire array and output the lowest number in each array? For example [[4, 6][8, 3]].My attempts at using .min, <=>, and if else statements have not worked.
Assuming you want to list 'all the mins from the arrays', there are lots of ways, here is a simple one:
array_of_arrays = [[4,6],[8,3]]
lowest_arrays = array_of_arrays.map {|a| a[0] < a[1] ? a[0] : a[1]}
or
lowest_arrays = array_of_arrays.map {|a| a.min}
This outputs [4, 3]
Should be as simple as:
[[4, 6],[8, 3]].each{|a| puts a.min}
or
[[4, 6],[8, 3]].map{|a| a.min}
... for an array output
Just use flatten followed by min like so:
[[4, 6], [8, 3]].flatten.min
=> 3

How can I sort by multiple conditions with different orders?

I'd really like to handle this without monkey-patching but I haven't been able to find another option yet.
I have an array (in Ruby) that I need to sort by multiple conditions. I know how to use the sort method and I've used the trick on sorting using an array of options to sort by multiple conditions. However, in this case I need the first condition to sort ascending and the second to sort descending. For example:
ordered_list = [[1, 2], [1, 1], [2, 1]]
Any suggestions?
Edit: Just realized I should mention that I can't easily compare the first and second values (I'm actually working with object attributes here). So for a simple example it's more like:
ordered_list = [[1, "b"], [1, "a"], [2, "a"]]
How about:
ordered_list = [[1, "b"], [1, "a"], [2, "a"]]
ordered_list.sort! do |a,b|
[a[0],b[1]] <=> [b[0], a[1]]
end
I was having a nightmare of a time trying to figure out how to reverse sort a specific attribute but normally sort the other two. Just a note about the sorting for those that come along after this and are confused by the |a,b| block syntax. You cannot use the {|a,b| a.blah <=> b.blah} block style with sort_by! or sort_by. It must be used with sort! or sort. Also, as indicated previously by the other posters swap a and b across the comparison operator <=> to reverse the sort order. Like this:
To sort by blah and craw normally, but sort by bleu in reverse order do this:
something.sort!{|a,b| [a.blah, b.bleu, a.craw] <=> [b.blah, a.bleu, b.craw]}
It is also possible to use the - sign with sort_by or sort_by! to do a reverse sort on numerals (as far as I am aware it only works on numbers so don't try it with strings as it just errors and kills the page).
Assume a.craw is an integer. For example:
something.sort_by!{|a| [a.blah, -a.craw, a.bleu]}
I had this same basic problem, and solved it by adding this:
class Inverter
attr_reader :o
def initialize(o)
#o = o
end
def <=>(other)
if #o.is && other.o.is
-(#o <=> other.o)
else
#o <=> other.o
end
end
end
This is a wrapper that simply inverts the <=> function, which then allows you to do things like this:
your_objects.sort_by {|y| [y.prop1,Inverter.new(y.prop2)]}
Enumerable#multisort is a generic solution that can be applied to arrays of any size, not just those with 2 items. Arguments are booleans that indicate whether a specific field should be sorted ascending or descending (usage below):
items = [
[3, "Britney"],
[1, "Corin"],
[2, "Cody"],
[5, "Adam"],
[1, "Sally"],
[2, "Zack"],
[5, "Betty"]
]
module Enumerable
def multisort(*args)
sort do |a, b|
i, res = -1, 0
res = a[i] <=> b[i] until !res.zero? or (i+=1) == a.size
args[i] == false ? -res : res
end
end
end
items.multisort(true, false)
# => [[1, "Sally"], [1, "Corin"], [2, "Zack"], [2, "Cody"], [3, "Britney"], [5, "Betty"], [5, "Adam"]]
items.multisort(false, true)
# => [[5, "Adam"], [5, "Betty"], [3, "Britney"], [2, "Cody"], [2, "Zack"], [1, "Corin"], [1, "Sally"]]
I've been using Glenn's recipe for quite a while now. Tired of copying code from project to project over and over again, I've decided to make it a gem:
http://github.com/dadooda/invert

Resources