3

I have an array of hashes, id being one of the keys in the hashes. I want to sort the array elements according to a given order of ID values.

Suppose my array(size=5) is:

[{"id"=>1. ...}, {"id"=>4. ...}, {"id"=>9. ...}, {"id"=>2. ...}, {"id"=>7. ...}]

I want to sort the array elements such that their ids are in the following order:

[1,3,5,7,9,2,4,6,8,10]

So the expected result is:

[{'id' => 1},{'id' => 7},{'id' => 9},{'id' => 2},{'id' => 4}]

5 Answers 5

7

Here is a solution for any custom index:

def my_index x 
  # Custom code can be added here to handle items not in the index.
  # Currently an error will be raised if item is not part of the index.
  [1,3,5,7,9,2,4,6,8,10].index(x) 
end

my_collection = [{"id"=>1}, {"id"=>4}, {"id"=>9}, {"id"=>2}, {"id"=>7}]
p my_collection.sort_by{|x| my_index x['id'] } #=> [{"id"=>1}, {"id"=>7}, {"id"=>9}, {"id"=>2}, {"id"=>4}]

Then you can format it in any way you want, maybe this is prettier:

my_index = [1,3,5,7,9,2,4,6,8,10]
my_collection.sort_by{|x| my_index.index x['id'] }
Sign up to request clarification or add additional context in comments.

Comments

1

I would map the hash based on the values like so:

a = [{"id"=>1}, {"id"=>4}, {"id"=>9}, {"id"=>2}, {"id"=>7}]

[1,3,5,7,9,2,4,6,8,10].map{|x| a[a.index({"id" => x})] }.compact

#=> [{"id"=>1}, {"id"=>7}, {"id"=>9}, {"id"=>2}, {"id"=>4}]

Comments

1

General note on sorting. Use #sort_by method of the 's class:

[{'id' => 1},{'id'=>3},{'id'=>2}].sort_by {|x|x['id'] }
# => [{"id"=>1}, {"id"=>2}, {"id"=>3}]

Or with usage #values method as a callback:

[{'id' => 1},{'id'=>3},{'id'=>2}].sort_by(&:values)
# => [{"id"=>1}, {"id"=>2}, {"id"=>3}]

or you can use more obvious version with #sort method:

[{'id' => 1},{'id'=>3},{'id'=>2}].sort {|x,y| x['id'] <=> y['id'] }
# => [{"id"=>1}, {"id"=>2}, {"id"=>3}]

For your case, to sort with extended condition use #% to split even and odd indexes:

[{'id' => 1},{'id'=>4},{'id'=>9},{'id'=>2},{'id'=>7}].sort do |x,y|
    u = y['id'] % 2 <=> x['id'] % 2
    u == 0 && y['id'] <=> x['id'] || u 
end
# => [{"id"=>1}, {"id"=>7}, {"id"=>9}, {"id"=>2}, {"id"=>4}]

For your case, to sort with extended condition use #% to split according the index, even id value is absent in the index array:

index = [1,3,5,7,4,2,6,8,10] # swapped 2 and 4, 9 is absent

[{'id' => 1},{'id'=>4},{'id'=>9},{'id'=>2},{'id'=>7}].sort do |x,y|
   !index.rindex( x[ 'id' ] ) && 1 || index.rindex( x[ 'id' ] ) <=> index.rindex( y[ 'id' ] ) || -1 
end
# => [{"id"=>1}, {"id"=>7}, {"id"=>4}, {"id"=>2}, {"id"=>9}]

6 Comments

Expected result is [{'id' => 1},{'id' => 7},{'id' => 9},{'id' => 2},{'id' => 4}]
@Малъ i love clean solutions.
@МалъСкрылевъ This would work in this particular case(odds then evens), but is there a general solution. i may have a case where the desirable order will be random
@МалъСкрылевъ : example the desired order is [1,5,3,2,8,9,7,6,4]
So, take a look at my answer where I already answered the question.
|
0

Why not just sort?

def doit(arr, order)
  arr.sort { |h1,h2| order.index(h1['id']) <=> order.index(h2['id']) }
end

order = [1,3,5,7,9,2,4,6,8,10]
arr = [{'id' => 1}, {'id' => 4}, {'id' => 9}, {'id' => 2}, {'id' => 7}]     

doit(arr, order)
  #=> [{'id' => 1}, {'id' => 7}, {'id' => 9}, {'id' => 2}, {'id' => 4}]     

Comments

0
a= [{"id"=>1}, {"id"=>4}, {"id"=>9}, {"id"=>2}, {"id"=>7}]
b=[1,3,5,7,9,2,4,6,8,10]
a.sort_by{|x| b.index (x['id'])}

Comments

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.