views:

180

answers:

2

A user has defined the order of columns which is in an array.

order = [:col1, :col2, :col3]

Since the user defined columns order the state of table has changed and the current list of columns is

cols = [:col1, :col4, :col3, :col5]

With the given order cols need to sorted. In this case the sorted columns could look like one of these two cases.

[:col2, :col3, :col4, :col5] 
[:col2, :col3, :col5, :col4]

Here is my code to get it working. Wondering if there is a better way.

#get rid of :col2 since it is not present in the cols listing
sanitized_order = order - (order - cols)

sorted_cols = sanitized_order + (cols - sanitized_order)
+2  A: 

What do you mean by better? You already accomplished your task pretty handily.

1) This is like yours but 1 readable line.

#order & cols -> provides unique items found in both
#cols - order -> provides columns that are in cols but not order
sorted_cols = (order & cols) + (cols - order)

2) Here is a way that reads more like a book so someone could follow it line by line to see the logic instead of figuring out the differences in tables:

order = [:col1, :col2, :col3]
cols = [:col1, :col4, :col3, :col5]

sanitized_order = []

order.each do |column|
  if cols.include?(column) then
    sanitized_order << column
    cols.delete(column)
  end
end

cols.each do |remainingcolumn|
  sanitized_order << remainingcolumn
end

puts sanitized_order
Beanish
A: 

Here's another wordy way to do it:

order = [:col1, :col2, :col3]
cols = [:col3, :col2, :col5, :col4]
sorted_order = cols.sort_by do |c|
  if order.index(c)
    [1, order.index(c)]
  else
    [2, cols.index(c)]
  end
end
p sorted_order    # => [:col2, :col3, :col5, :col4]

Here's how it works. sort_by yields elements of an array to the block; the block should then return something comparable (technically, something that responds to the <=> operator). sort_by uses the <=> operator on the results returned by the block to decide what order the array should be in.

The <=> (spaceship) operator, as you may know, is a binary operator taking two elements a and b. If a < b, it returns -1. If a == b, it returns 0. If a > b, it returns +1.

Arrays respond in an unsurprising way to the <=> operator. The elements of the left array are compared with the elements of the right array in turn, starting with index 0 and increasing. If the a[i] <=> b[i] is != 0, return that result, but if the result is 0, examine the next element. If the last pair of elements compared is 0 (equal) and the arrays are the same size, the arrays are equal and Array.<=> returns 0, otherwise the longer array is considered larger (this example always returns equal size arrays, however).

So, for example:

[2, 1] <=> [2, 2] == -1
[2, 2] <=> [2, 2] == 0
[2, 3] <=> [2, 2] == +1
[1, 2] <=> [2, 1] == +1

So, inside a sort_by, we can use the elements of an array a to indicate primary, secondary, tertiary etc. sort order. a[0] is the primary sort order, a[1] is the secondary sort order, and so on.

All of the columns the customer specified should come first. So, for each column, find its index in customer specified order (order). If that returns a number, then we know that the customer specified that column, and we know its index in the customer-specified list. The primary sort order is 1, because we want the customer specified columns to come first; the secondary sort order is the index, since that gives us the order the customer specified the columns.

If we don't find the column in the order array (that is, order.index(c) returns nil), then we'll 2 as the primary sort order and the index in the master column list (cols) as the secondary sort order. That way all columns the customer did not specify will be last, but in the order they are specified in the laster column list.

Wayne Conrad
cool. This will work. However I think it is not so obvious what's happening.
Roger
I added an explanation. I hope that helps.
Wayne Conrad