0

I have a big array of hashes:

array = [
  {color: '5 absolute', ... },
  {color: '5.0', ... },
  {color: '5.1', ... },
  {color: 'last', ... },
  {color: '50', ... },
  {color: '5 elite', ... },
  {color: 'edge'}
]

I need colors to ordered:

5 absolute
5 elite
5.0
5.1
50
edge
last

The priority is:

first going spaces ' ',
then dots '.',
then digits '7',
then other 'string'

This is like SQL activerecord analog query, but I don't want that difficult query in the background. I want this logic. How can I do this using AR query?

3
  • In the sorted data, where would you want '60 something' to be inserted? Commented Nov 11, 2013 at 10:56
  • Yes, I wish to see 6 before 50 =) Commented Nov 11, 2013 at 11:51
  • But before of after the entries with dots? Commented Nov 11, 2013 at 11:57

3 Answers 3

1

You could always just sort the array of hashes.

array.map{|h| h[:color]}.sort
=> ["5 absolute", "5 elite", "5.0", "5.1", "50", "edge", "last"]

The following first sorts by number and then by the string after the number.

array = [{color: '5 absolute'}, {color: '5.0'}, {color: '5.1'}, 
         {color: 'last'}, {color: '50'}, {color: '5 elite'}, 
         {color: 'edge'}, {color: '6 absolute'}, {color: '7'}]

array.map{|h| h[:color]}.sort_by do |s|
  n = s.to_f
  if n == 0 && s.match(/\d/).nil?
    n = Float::INFINITY
  end
  [n, s.split(" ")[-1]]
end
=> ["5.0", "5 absolute", "5 elite", "5.1", "6 absolute", "7", "50", "edge", "last"]
Sign up to request clarification or add additional context in comments.

2 Comments

Probably because I didn't provide an AR query.
Please describe that in your question. The sort order you gave says nothing about sorting by the number first.
0

so like this?

h = [{:color=>"5 absolute"},
 {:color=>"5.0"},
 {:color=>"5.1"},
 {:color=>"last"},
 {:color=>"50"},
 {:color=>"5 elite"},
 {:color=>"edge"}]

h.map(&:values).flatten.sort
# => ["5 absolute", "5 elite", "5.0", "5.1", "50", "edge", "last"]

or all the other answers...

Comments

0

From you question it is very hard to tell what you want. Especially since the order you ask for is exactly the same one a normal sort would create.

I any case, here is a way of creating a "custom sort" order the way you wanted. The difference between this and a regular sort is that in this sort can make certain types of characters or sets of characters triumph others.

array = [
  {color: '5 absolute'},
  {color: '5.0'},
  {color: '50 hello'},
  {color: 'edge'}
]
p array.sort_by{|x| x[:color]} #=> [{:color=>"5 absolute"}, {:color=>"5.0"}, {:color=>"50 hello"}, {:color=>"edge"}]
# '50 hello' is after '5.0' as . is smaller than 0.

Solving this problem is a bit tricky, here is how I would do it:

# Create a custom sort order using regexp:
# [spaces, dots, digits, words, line_endings]
order  = [/\s+/,/\./,/\d+/,/\w+/,/$/]

# Create a union to use in a scan:
regex_union = Regexp.union(*order)

# Create a function that maps the capture in the scan to the index in the custom sort order:
custom_sort_order = ->x{
 x[:color].scan(regex_union).map{|x| [order.index{|y|x=~y}, x]}.transpose
}

#Sort:
p array.sort_by{|x| custom_sort_order[x]}
# => [{:color=>"5 absolute"}, {:color=>"50 hello"}, {:color=>"5.0"}, {:color=>"edge"}]

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.