SketchUp / api-issue-tracker

Public issue tracker for the SketchUp and LayOut's APIs
https://developer.sketchup.com/
38 stars 10 forks source link

REQ: Sketchup::InstancePath#prune #700

Open DanRathbun opened 3 years ago

DanRathbun commented 3 years ago

SketchUp API Feature Request

Sketchup::InstancePath#prune

The proposed method is basically the inverse of Enumerable#drop.

I've come across several situations where I needed to pop off the last item of a collection (similar to Array#pop,) but return the new collection rather than the popped item. But it is clunky doing self.to_a.reverse.drop(n).reverse every place I need this.

I added it to my set of refinements for enumerable classes, and then thought to add it for InstancePaths. This allows doing...

new_path = prev_path.prune

in place of ...

new_path = Sketchup::InstancePath::new(prev_path.to_a[0...-1])

Here (for what it's worth,) is my Ruby refinement for implementing a #prune method on instance paths. (As much a place to record and show it, as anything else.)

module Refined_InstancePath

  refine ::Sketchup::InstancePath do

    # Return a new InstancePath object by pruning from the end of
    # this instance path.
    # @param num [Integer] (1) the number of items to prune.
    # @return [Sketchup::InstancePath] the new path object.
    def prune(num = 1)
    # Similar to Array#pop, but does not return the popped item:
      unless num.respond_to?(to_i)
        fail(TypeError,'Integer argument expected.',caller)
      end
      num = num.to_i
      unless num > 0
        fail(ArgumentError,'Argument expected to be > 0.',caller)
      end
      # This is the inverse of Enumerable#drop:
      # path = self.to_a.reverse.drop(num).reverse
      path = self.to_a[0...-num]
      # If num exceeeds the number of items, path will be []:
      self.class.new(path)
    end

  end # class refinement

end # refinement module

Note: There might also benefit for a prune! method that modifies the receiver, but I do not see any way to do this with Ruby.

DanRathbun commented 3 years ago

Just for a bit more useless information... ;) ... the closest "pruning" method I've found for the Ruby core Array class is Array#slice!(-n,n) which although it affects the receiver array, returns the pruned items rather than the receiver.

This has led me to concoct the following Array class refinement where I follow closely how #slice and [#slice!]() operate, but add a boolean chain argument to the latter that will return the receiver for call chaining:

module SomeLibrary::Refined::Array

  refine ::Array do

    # Return a new Array object by pruning from the end of this one.
    #
    # If num exceeds the number of items, the new array will be empty.
    #
    # @param num [Integer] (1) the number of items to prune.
    #
    # @return [Array] the new array object.
    #
    def prune(num = 1)
      unless num.respond_to?(to_i)
        fail(::TypeError, 'Integer argument expected.', caller)
      end
      num = num.to_i
      unless num > 0
        fail(::ArgumentError, 'Argument expected to be > 0.', caller)
      end
      #
      self[0...-num]
    end

    # Prunes off the number of items given from the end of this array,
    # and (as a default) returns the pruned items in a new array. This is the
    # inverse of {Enumerable#drop}.
    #
    # If num exceeds the number of items nothing is removed from this array.
    #
    # @param num [Integer] (1) the number of items to prune.
    # @param chain [Boolean] (`false`) If `true`, will return this array
    #  for call chaining rather than the pruned items.
    #
    # @return [Array] this array itself.
    #
    def prune!(num = 1, chain = false)
      unless num.respond_to?(to_i)
        fail(::TypeError, 'Integer argument expected.', caller)
      end
      num = num.to_i
      unless num > 0
        fail(::ArgumentError, 'Argument expected to be > 0.', caller)
      end
      # Basically do: self.reverse.drop(num).reverse
      prunings = self.slice!(-num,num)
      chain ? self : prunings
    end

  end # class refinement

end # refinement module SomeLibrary::Refined::Array
DanRathbun commented 3 years ago

RELATED ISSUE #339: Just noticed Thomas' Mutators for InstancePath proposal.