Compare and sort one array according to another

I have two arrays of objects that look something like this:

arr1 = [
Obj1(prop1, prop2, id),
Obj2(prop1, prop2, id),
Obj3(prop1, prop2, id)
]

arr2 = [
OthrObj1(prop1, prop2, ref_id),
OthrObj2(prop1, prop2, ref_id),
OthrObj3(prop1, prop2, ref_id)
]

The objects in arr1 are ordered by id (just as an example). The
objects in arr2 have a 1-1 relationship with the objects in arr1,
however they are not sorted. They need to be sorted to match the order
of the objects in arr1. The order of arr1 is not predictable so arr2 has
to be compared to arr1 to find the order. The two arrays are always the
same size. No items should repeat, however this might be something i
need to guard against. For now let’s assume no items repeat.

My current solution is to make a temp array and then build it with the
items in order. It’s ok but i’m wondering if there’s a better ruby
method.

temp_arr = []

arr1.each do |o|
index = arr2.find_index {|ele| ele.ref_id == o.id}
temp_arr.push arr2[index] if index
end

If the items within each array belong to each other, how about a
multidimensional array?

arr = [
[ Obj1(prop1, prop2, id), OthrObj1(prop1, prop2, ref_id) ],
[ Obj2(prop1, prop2, id), OthrObj2(prop1, prop2, ref_id) ],
[ Obj3(prop1, prop2, id), OthrObj3(prop1, prop2, ref_id) ]
]

All the sorting is dealt with together then:

sorted_arr = arr.sort_by { |obj1, obj2| obj1.id }

Joel P. wrote in post #1112665:

If the items within each array belong to each other, how about a
multidimensional array?

arr = [
[ Obj1(prop1, prop2, id), OthrObj1(prop1, prop2, ref_id) ],
[ Obj2(prop1, prop2, id), OthrObj2(prop1, prop2, ref_id) ],
[ Obj3(prop1, prop2, id), OthrObj3(prop1, prop2, ref_id) ]
]

All the sorting is dealt with together then:

sorted_arr = arr.sort_by { |obj1, obj2| obj1.id }

I don’t quite understand everything that’s happening here. Can you
elaborate a bit more on your solution.

On Mon, Jun 17, 2013 at 5:43 PM, masta Blasta [email protected]
wrote:

OthrObj2(prop1, prop2, ref_id),

My current solution is to make a temp array and then build it with the
items in order. It’s ok but i’m wondering if there’s a better ruby
method.

Something like this?

arr2.sort_by {|o| arr1.find_index {|a| o.prop1 == a.prop1 && o.prop2 ==
a.prop2}}

Kind regards

robert

masta Blasta wrote in post #1112677:

Joel P. wrote in post #1112665:

If the items within each array belong to each other, how about a
multidimensional array?

arr = [
[ Obj1(prop1, prop2, id), OthrObj1(prop1, prop2, ref_id) ],
[ Obj2(prop1, prop2, id), OthrObj2(prop1, prop2, ref_id) ],
[ Obj3(prop1, prop2, id), OthrObj3(prop1, prop2, ref_id) ]
]

All the sorting is dealt with together then:

sorted_arr = arr.sort_by { |obj1, obj2| obj1.id }

I don’t quite understand everything that’s happening here. Can you
elaborate a bit more on your solution.

I’m going by your comment here:
“The objects in arr2 have a 1-1 relationship with the objects in arr1”

If the objects are interlinked, rather than trying to keep two separate
arrays and sorting them side-by-side (which can easily go wrong), you
could simply connect the pairs into their own arrays all inside another
array.

This has the benefit that once you put the pair into their own array,
they are connected without you having to worry about having to do
everything the same with array1 and array2.

Then you can iterate through the large array like this:

arr.each { |subarray| some_method( subarray[0], subarray[1] ) }

In this example you can access each of the smaller arrays inside your
array. [0] is each object and [1] is each otherobject

Or like this:

arr.each { |object, otherobject| some_method( object, otherobject ) }

This example is functionally the same as the previous one, but you’re
using parallel assignment to place each object from the array into its
own variable so it’s easier to read.

Using this approach, you can sort the larger array containing objects
and otherobjects by whatever criteria you want, and they’ll stay
together as a pair within the larger array.

This will sort each array within the parent by the first object’s id:

arr = arr.sort_by { |object, otherobject| object.id }

This does the same thing, but with the second object’s id:

arr = arr.sort_by { |object, otherobject| otherobject.id }

The best way to grasp how this is works is to do it in IRB and play
around with the results.

Look at Robert’s suggestion for how to sort arrays compared to each
other. His suggestions are usually (perhaps always) the best solution to
the problem.

Here’s his solution converted for your recent example:
arr2.sort_by {|o| terms.index {|a| a[:id] == o[:term_id] }}

My thinking was that you could handle the object creation differently,
but that may not be suitable in your case. There’s always the option to
conjoin the objects after sorting them (see the Array#zip method) in
order to keep them together when you continue onto the next task.
Using a sort just seems messy to me, because you’re relying heavily on
there being a matched pair in each case with no duplicates or missing
objects. Still, it might be exactly the right thing to do in your
scenario. There’s more than one way to do it :slight_smile:

Joel P. wrote in post #1112686:

masta Blasta wrote in post #1112677:

Joel P. wrote in post #1112665:

If the items within each array belong to each other, how about a
multidimensional array?

arr = [
[ Obj1(prop1, prop2, id), OthrObj1(prop1, prop2, ref_id) ],
[ Obj2(prop1, prop2, id), OthrObj2(prop1, prop2, ref_id) ],
[ Obj3(prop1, prop2, id), OthrObj3(prop1, prop2, ref_id) ]
]

All the sorting is dealt with together then:

sorted_arr = arr.sort_by { |obj1, obj2| obj1.id }

I don’t quite understand everything that’s happening here. Can you
elaborate a bit more on your solution.

I’m going by your comment here:
“The objects in arr2 have a 1-1 relationship with the objects in arr1”

If the objects are interlinked, rather than trying to keep two separate
arrays and sorting them side-by-side (which can easily go wrong), you
could simply connect the pairs into their own arrays all inside another
array.

This has the benefit that once you put the pair into their own array,
they are connected without you having to worry about having to do
everything the same with array1 and array2.

Then you can iterate through the large array like this:

arr.each { |subarray| some_method( subarray[0], subarray[1] ) }

In this example you can access each of the smaller arrays inside your
array. [0] is each object and [1] is each otherobject

Or like this:

arr.each { |object, otherobject| some_method( object, otherobject ) }

This example is functionally the same as the previous one, but you’re
using parallel assignment to place each object from the array into its
own variable so it’s easier to read.

Using this approach, you can sort the larger array containing objects
and otherobjects by whatever criteria you want, and they’ll stay
together as a pair within the larger array.

This will sort each array within the parent by the first object’s id:

arr = arr.sort_by { |object, otherobject| object.id }

This does the same thing, but with the second object’s id:

arr = arr.sort_by { |object, otherobject| otherobject.id }

The best way to grasp how this is works is to do it in IRB and play
around with the results.

I just learned something new about ordering arrays! However this doesn’t
actually solve the original problem. Your solution expects both arrays
to be properly sorted, and then keeps them together if one or the other
is re-ordered.

Originally only one of the arrays was properly sorted, and the other had
then to be ordered to match.

Here is some better sample data:
terms = [
{:id=>3, :name=>“term1”, :version=>2},
{:id=>4, :name=>“term2”, :version=>2},
{:id=>8, :name=>“term3”, :version=>1}
]

arr2 = [
{:term_id=>4, :name=>“assoc1”},
{:term_id=>8, :name=>“assoc2”},
{:term_id=>3, :name=>“assoc3”}
]

so ‘terms’ is properly sorted, and now we want arr2 to follow that
order. So after our operation arr2 should look like:

arr2 = [
{:term_id=>3, :name=>“assoc3”},
{:term_id=>4, :name=>“assoc1”},
{:term_id=>8, :name=>“assoc2”}

]

These are ActiveRecord objects. A SQL solution is possible but the query
is painfully long due to complex associations. Since the arrays are
usually small 3-5 records, i was hoping i could just sort them
programmatically.

On Mon, Jun 17, 2013 at 10:39 PM, Jess Gabriel y Galn
[email protected] wrote:

[ Obj3(prop1, prop2, id), OthrObj3(prop1, prop2, ref_id) ]
I’m going by your comment here:

arr = arr.sort_by { |object, otherobject| object.id }
to be properly sorted, and then keeps them together if one or the other
]
arr2 = [

Did you try Robert K.'s solution? Translated to this new example:

Typo: that should have been

On Mon, Jun 17, 2013 at 9:56 PM, masta Blasta [email protected]
wrote:

]
“The objects in arr2 have a 1-1 relationship with the objects in arr1”
Then you can iterate through the large array like this:
This example is functionally the same as the previous one, but you’re

is re-ordered.

{:term_id=>3, :name=>"assoc3"},
{:term_id=>4, :name=>"assoc1"},
{:term_id=>8, :name=>"assoc2"}

]

These are ActiveRecord objects. A SQL solution is possible but the query
is painfully long due to complex associations. Since the arrays are
usually small 3-5 records, i was hoping i could just sort them
programmatically.

Did you try Robert K.'s solution? Translated to this new example:

arr2.sort_by {|o| arr1.find_index {|a| o[:term_id] == a[:id]}}

=> [{:term_id=>3, :name=>“assoc3”}, {:term_id=>4, :name=>“assoc1”},
{:term_id=>8, :name=>“assoc2”}]

Jesus.

You have said that the size of your two arrays is small, so the fact
that
the find_index based solution is o(n^2) probably doesn’t matter. For
kicks, a o(n) solution compared to the o(n^2) solution here:

It involves precomputing a lookup index for the data you need to sort.

-Doug

On Mon, Jun 17, 2013 at 1:40 PM, Jess Gabriel y Galn <

On Mon, Jun 17, 2013 at 11:35 PM, Douglas S. [email protected]
wrote:

You have said that the size of your two arrays is small, so the fact that
the find_index based solution is o(n^2) probably doesn’t matter.

Yes, that’s what I was figuring. Your analysis is spot on.

For kicks, a o(n) solution compared to the o(n^2) solution here:

Sorting arrays of related objects · GitHub

It involves precomputing a lookup index for the data you need to sort.

Your “gen_lookup:” is exactly what I’d did for larger arrays.

Kind regards

robert

Robert K. wrote in post #1112735:

On Mon, Jun 17, 2013 at 11:35 PM, Douglas S. [email protected]
wrote:

You have said that the size of your two arrays is small, so the fact that
the find_index based solution is o(n^2) probably doesn’t matter.

Yes, that’s what I was figuring. Your analysis is spot on.

For kicks, a o(n) solution compared to the o(n^2) solution here:

Sorting arrays of related objects · GitHub

It involves precomputing a lookup index for the data you need to sort.

Your “gen_lookup:” is exactly what I’d did for larger arrays.

Kind regards

robert

This has been a great little learning exercise for me. I had never quite
gotten how the sort_by method worked, and now reading it over again with
the example it’s perfectly clear.

I’m surprised at how slow the method is. In the benchmark, 1.24s for 10
records…?? That’s huge! The SQL query to fetch the records already
sorted, as big and messy as it is, still only takes 10ms. I might have
to stick with SQL on this one since for a Rails app response time is
critical.

I’m certainly glad I came and asked for help about this.

On Tue, 18 Jun 2013 19:46:36 +0200, masta Blasta [email protected]
wrote:

I’m surprised at how slow the method is. In the benchmark, 1.24s for 10
records…?? That’s huge!

I think that’s for 100 000 repetitions of the test.

Bartosz Dziewoński wrote in post #1112795:

On Tue, 18 Jun 2013 19:46:36 +0200, masta Blasta [email protected]
wrote:

I’m surprised at how slow the method is. In the benchmark, 1.24s for 10
records…?? That’s huge!

I think that’s for 100 000 repetitions of the test.

facepalm