可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have an array of objects that I need to sort by a position attribute that could be an integer or nil, and I need the objects that have the nil position to be at the end of the array. Now, I can force the position to return some value rather than nil so that the array.sort doesn't fail, but if I use 0 as this default, then it puts those objects at the front of the sort. What's the best way to to do this sort? should I just set the nil values to some ridiculously high number that is 'almost' always guaranteed to be at the end? or is there some other way i could cause the array.sort method to put the nil attribute objects at the end of the array? the code looks like this:
class Parent
def sorted_children
children.sort{|a, b| a.position <=> b.position}
end
end
class Child
def position
category ? category.position : #what should the else be??
end
end
now, if i make the 'else' something like 1000000000, then it's most likely gonna put them at the end of the array, but I don't like this solution as it's arbitrary
回答1:
How about in Child
defining <=>
to be based on category.position
if category
exists, and sorting items without a category
as always greater than those with a category
?
class Child
# Not strictly necessary, but will define other comparisons based on <=>
include Comparable
def <=> other
return 0 if !category && !other.category
return 1 if !category
return -1 if !other.category
category.position <=> other.category.position
end
end
Then in Parent
you can just call children.sort
.
回答2:
I would just tweak your sort to put nil
items last. Try something like this.
foo = [nil, -3, 100, 4, 6, nil, 4, nil, 23]
foo.sort { |a,b| a && b ? a <=> b : a ? -1 : 1 }
=> [-3, 4, 4, 6, 23, 100, nil, nil, nil]
That says: if a and b are both non-nil sort them normally but if one of them is nil, return a status that sorts that one larger.
回答3:
I handle these kinds of things like this:
children.sort_by {|child| [child.position ? 0 : 1,child.position || 0]}
回答4:
To be fair, I'm not very familiar with Ruby, so take this as more of an algorithm idea rather than a code one... and rewrite the ?: operator as whatever Ruby has that's cleaner.
Can't you just check for nil in the comparison:
class Parent
def sorted_children
children.sort{|a,b|( a and b ) ? a <=> b : ( a ? -1 : 1 ) }
end
end
Edited to use Glenra's code, which implements the same thing as mine but in a smaller (and probably easier to read) amount of code.
回答5:
I haven't done Ruby in a while, but you could split the null-checking from the sorting (and just allow Child#position to return null):
def sorted_children
children.reject{|c| c.position.nil?}.sort_by(&:position) +
children.select{|c| c.position.nil?}
end
Admittedly it's not the most efficient solution, but it doesn't have any magic numbers.
回答6:
You can do this without overriding the spaceship operator by defining a new comparison method.
class Child
include Comparable
def compare_by_category(other)
return 0 if !category && !other.category
return 1 if !category
return -1 if !other.category
category.position <=> other.category.position
end
end
The sort
method can take a block, so you can then sort using this new method:
children.sort {|a,b| a.compare_by_category(b) }
回答7:
The most simple solution for me is
def sorted_children(children)
children.sort_by { |child| child.position || -1}
end