7

There are two arrays:

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

I want to sort B in a way that for all the elements of B that exists in A, sort the elements in the order that is in array A.

The desired sorted resulted would be

B #=> [1, 2, 3, 4, 5, 6]

I have tried to do

B = B.sort_by { |x| A.index }

but it does not work.

This question differs from the possible duplicates because it deals with presence of elements in the corresponding array and no hashes are present here.

3
  • 1
    "elements of B that exists in array A" – what about elements that don't exist in A? Commented Jun 23, 2016 at 12:30
  • 1
    Possible duplicate of Sort an array according to the elements of another array Commented Jun 23, 2016 at 16:53
  • I have placed my comment regarding possible duplicate @WandMaker Commented Jun 23, 2016 at 23:44

4 Answers 4

27

It perfectly works:

▶ A = [1,3,2,6,4,5,7,8,9,10]
▶ B = [3,4,1,5,2,6]
▶ B.sort_by &A.method(:index)
#⇒ [1, 3, 2, 6, 4, 5]

If there could be elements in B that are not present in A, use this:

▶ B.sort_by { |e| A.index(e) || Float::INFINITY }
Sign up to request clarification or add additional context in comments.

6 Comments

Looks like there is no need to make the check that I did in my answer :) Good answer.
Although it fails for elements in B that are not in A. Try to delete element 3 from A
Works perfect ! Thanks
Works until you don't have elements from B in A.
@radubogdan Good catch, updated. I do not think that your decision to drop inexisting in A elements is OK.
|
3

I would start by checking what elements from B exist in A :

B & A

and then sort it:

(B & A).sort_by { |e| A.index(e) }

Comments

2

First consider the case where every element of B is in A, as with the question's example:

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

One could write the following, which requires only a single pass through A (to construct g1) and a single pass through B.

g = A.each_with_object({}) { |n,h| h[n] = 1 }
  #=> {1=>1, 2=>1, 3=>1, 4=>1, 5=>1, 6=>1, 7=>1, 8=>1, 9=>1, 10=>1}
B.each_with_object(g) { |n,h| h[n] += 1 }.flat_map { |k,v| [k]*(v-1) }
  #=> [1, 1, 1, 2, 3, 5, 6, 6]

If there is no guarantee that all elements of B are in A, and any that are not are to be placed at the end of the sorted array, one could change the calculation of g slightly.

g = (A + (B-A)).each_with_object({}) { |n,h| h[n] = 1 }

This requires one more pass through A and through B.

Suppose, for example,

A = [2,3,4,6,7,8,9]

and B is unchanged. Then,

g = (A + (B-A)).each_with_object({}) { |n,h| h[n] = 1 }
  #=> {2=>1, 3=>1, 4=>1, 6=>1, 7=>1, 8=>1, 9=>1, 1=>1, 5=>1}
B.each_with_object(g) { |n,h| h[n] += 1 }.flat_map { |k,v| [k]*(v-1) }
  #=> [2, 3, 6, 6, 1, 1, 1, 5]

This solution demonstrates the value of a controversial change to hash properties that were made in Ruby v1.9: hashes would thereafter be guaranteed to maintain key-insertion order.

1 I expect one could write g = A.product([1]).to_h, but the doc Array#to_h does not guarantee that the keys in the hash returned will have the same order as they do in A.

2 Comments

If every element of B is in A, this solution is definitely the most elegant.
@mudasobwa, I made some changes that may interest you.
1

You just missed x in A.index, so the query should be:

B = B.sort_by { |x| A.index(x) }

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.