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