sorting a ruby array of objects by an attribute that could be nil

前端 未结 7 608
孤城傲影
孤城傲影 2020-12-08 04:23

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

相关标签:
7条回答
  • 2020-12-08 04:34

    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.

    0 讨论(0)
  • 2020-12-08 04:37

    I handle these kinds of things like this:

     children.sort_by {|child| [child.position ? 0 : 1,child.position || 0]}
    
    0 讨论(0)
  • 2020-12-08 04:44

    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) }
    
    0 讨论(0)
  • 2020-12-08 04:44

    The most simple solution for me is

    def sorted_children(children)
      children.sort_by { |child| child.position || -1}
    end
    
    0 讨论(0)
  • 2020-12-08 04:50

    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.

    0 讨论(0)
  • 2020-12-08 04:57

    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.

    0 讨论(0)
提交回复
热议问题