32

I want to sort an array in particular order given in another array.

EX: consider an array

a=["one", "two", "three"]
b=["two", "one", "three"]

Now I want to sort array 'a' in the order of 'b', i.e

a.each do |t|
  # It should be in the order of 'b'
  puts t
end

So the output should be

two
one 
three 

Any suggestions?

1
  • 2
    As it is, this question makes no sense. The example shows two arrays with the exact same values (only in different order). If you want to iterate the elements in a in the order found in b, well, iterate b and you're done :-) So I guess there are more conditions, maybe items in arrays do not match? is not a "==" between objects what you need but a different kind of equality? show some more meaningful examples. Commented Dec 14, 2012 at 8:14

3 Answers 3

59

Array#sort_by is what you're after.

a.sort_by do |element|
  b.index(element)
end

More scalable version in response to comment:

a=["one", "two", "three"]
b=["two", "one", "three"]

lookup = {}
b.each_with_index do |item, index|
  lookup[item] = index
end

a.sort_by do |item|
  lookup.fetch(item)
end
Sign up to request clarification or add additional context in comments.

8 Comments

The simplest for small arrays, but note that this is O(n^2) when the problem is O(n).
@tokland Ok. Supplied a more scalable version.
exactly, create an auxiliar mapping + sort_by. I'd probably write it lookup = Hash[b.to_enum.with_index], but well, that's just a detail. Btw, did you see my comment to the question? do you remember what the OP had in mind?
Yeah, equality on a given attribute is very common. And yeah, I know you are a friend of the &:obj.method(:name) trick, I remember your blog post about it :-)
We could also write lookup = b.each_with_index.to_h or lookup = b.map.with_index.to_h
|
12

If b includes all elements of a and if elements are unique, then:

puts b & a

3 Comments

If that condition holds then b&a == b.
@PanThomakos, It can include elements and also have others. %w{ameba bug bird dog horse shark} & %w{horse ameba shark} => ["ameba", "horse", "shark"]
I think it's cool that this works, but here are my reservations: 1. Using & (set intersection) to sort arrays is misleading. 2. The code is brittle. If the lists have duplicate elements or are of the wrong size the code breaks. If the implementation of & changes so that elements are no longer sorted the code breaks.
10

Assuming a is to be sorted with respect to order of elements in b

sorted_a = 
a.sort do |e1, e2|
  b.index(e1) <=> b.index(e2)
end

I normally use this to sort error messages in ActiveRecord in the order of appearance of fields on the form.

5 Comments

Why use sort when you can use sort_by?
Performance. ruby-doc.org/core/classes/Enumerable.html#M003120 Check out the benchmarking.
My benchmark shows N sec for sort, 0.07 N sec for sort_by and 0.01 N for &.
Because in sort_by indexes are calculated only once for each element, and in sort it could be possible only if it were some caching. And & is compiled in Ruby.
That benchmark in the doc compares sort without a block to sort_by using a (no-op) block. If you compare sort with a block (like the example in this answer) to the corresponding sort_by version you'll see that sort_by is considerably faster.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.