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

···

--
Posted via http://www.ruby-forum.com/.

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 }

···

--
Posted via http://www.ruby-forum.com/.

Something like this?

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

Kind regards

robert

···

On Mon, Jun 17, 2013 at 5:43 PM, masta Blasta <lists@ruby-forum.com> wrote:

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.

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

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:

···

--
Posted via http://www.ruby-forum.com/.

Joel Pearson 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.

···

--
Posted via http://www.ruby-forum.com/\.

masta Blasta wrote in post #1112677:

Joel Pearson 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.

···

--
Posted via http://www.ruby-forum.com/\.

Joel Pearson wrote in post #1112686:

masta Blasta wrote in post #1112677:

Joel Pearson 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.

···

--
Posted via http://www.ruby-forum.com/\.

Did you try Robert Klemme'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.

···

On Mon, Jun 17, 2013 at 9:56 PM, masta Blasta <lists@ruby-forum.com> wrote:

Joel Pearson wrote in post #1112686:

masta Blasta wrote in post #1112677:

Joel Pearson 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.

Joel Pearson wrote in post #1112686:

masta Blasta wrote in post #1112677:

Joel Pearson 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.

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

Typo: that should have been

···

On Mon, Jun 17, 2013 at 10:39 PM, Jesús Gabriel y Galán <jgabrielygalan@gmail.com> wrote:

On Mon, Jun 17, 2013 at 9:56 PM, masta Blasta <lists@ruby-forum.com> wrote:

arr2.sort_by {|o| terms.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, Jesús Gabriel y Galán < jgabrielygalan@gmail.com> wrote:

On Mon, Jun 17, 2013 at 10:39 PM, Jesús Gabriel y Galán > <jgabrielygalan@gmail.com> wrote:
> On Mon, Jun 17, 2013 at 9:56 PM, masta Blasta <lists@ruby-forum.com> > wrote:
>> Joel Pearson wrote in post #1112686:
>>> masta Blasta wrote in post #1112677:
>>>> Joel Pearson 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.
>
> Did you try Robert Klemme's solution? Translated to this new example:
>

Typo: that should have been

> arr2.sort_by {|o| terms.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.

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

···

On Mon, Jun 17, 2013 at 11:35 PM, Douglas Seifert <doug@dseifert.net> wrote:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Robert Klemme wrote in post #1112735:

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 Mon, Jun 17, 2013 at 11:35 PM, Douglas Seifert <doug@dseifert.net> > wrote:

--
Posted via http://www.ruby-forum.com/\.

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

···

On Tue, 18 Jun 2013 19:46:36 +0200, masta Blasta <lists@ruby-forum.com> wrote:

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

--
Matma Rex

Bartosz Dziewoński wrote in post #1112795:

···

On Tue, 18 Jun 2013 19:46:36 +0200, masta Blasta <lists@ruby-forum.com> > 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

--
Posted via http://www.ruby-forum.com/\.