Home > Net >  ruby reorder array of symbols in one pass
ruby reorder array of symbols in one pass

Time:12-15

I am looking for the most efficient way to do this. Anything with a hyphen must be in before any symbols without a hyphen in the array. My naive solution filters the array twice and concats. I feel like there should be a way to do this in one pass instead of two.

input = [:en, :de, :es, :"es-MX", :fr, :ko, :"ko-KR", :"en-GB"]

output = [:"es-MX", :"ko-KR", :"en-GB", :en, :de, :es, :fr]

Naive solution:

def reorder(input)
  ## find everything with a hypen
  output = input.select { |l| 
    l.to_s.include?('-')
  }

  # find everything without a hyphen and concat to output
  output.concat(input.reject { |l| 
    l.to_s.include?('-')
  })
end

CodePudding user response:

This is a sorting task, so it makes the most sense to me to simply use Enumerable#sort_by:

# Put symbols with hyphens first
input.sort_by { |s| s.to_s.include?('-') ? 0 : 1 }

If you want to sort by something else within those categories, you can do something like this:

# Put symbols with hyphens first, then sort in alphabetical order
input.sort_by { |s| [s.to_s.include?('-') ? 0 : 1, s] }

CodePudding user response:

You can filter the array in one pass with Array#partition:

def reorder(input)
  a, b = input.partition { |l| l.to_s.include?('-') }

  a.concat(b)
end

CodePudding user response:

input = [:en, :de, :es, :"es-MX", :fr, :ko, :"ko-KR", :"en-GB"]

code

p input.partition{|x|x.to_s.include?'-'}.flatten

Output

[:"es-MX", :"ko-KR", :"en-GB", :en, :de, :es, :fr, :ko]

CodePudding user response:

You're doing it in two passes as I can see.

With one pass only, you could add an if statement and check each element then push it on the left array if it contains the hypen, on the right array otherwise.

Then concat the arrays.

def reorder(input)
  left = []
  right = []
  input.each { |l| 
    if l.to_s.include?('-')
      left.push(l)
    else
      right.push(l)
    end
  }

  left.concat(right)
end

CodePudding user response:

Here's a way that makes a single pass through the array and preserves the ordering of symbols with hyphens and the ordering of symbols without hyphens.

def rearrange(arr)
  a = arr.dup
  pos = -1
  a.size.times { |i| a.insert(pos =1, a.delete_at(i)) if a[i].to_s.include?('-') }
  a
end
rearrange [:en, :de, :es, :"es-MX", :fr, :ko, :"ko-KR", :"en-GB"]
  #=> [:"es-MX", :"ko-KR", :"en-GB", :en, :de, :es, :fr, :ko]

The the use of Kernel#dup (to avoid mutating the original array) does not constitute a pass through the array because (as I understand) the C code copies blocks of memory.

CodePudding user response:

How about:

input.sort do |a,b|
  next a <=> b if a.to_s.include?('-') == b.to_s.include?('-')
  a.to_s.include?('-') ? -1 : 1
end
  • Related