hoodwink.d enhanced
RSS
2.0
XHTML
1.0

RedHanded

ary / 3 #

by why in bits

Sometimes I wish Array.partition could split into more than halves. Something like partition_by or commonality. Like an Array./ method!

 class Array
   def / len
     inject([]) do |ary, x|
       ary << [] if [*ary.last].nitems % len == 0
       ary.last << x
       ary
     end
   end
 end

 >> products = %w[cycles vents hoops willies moogs rifles pools fawns tridents]
 >> products / 3
 => [["cycles", "vents", "hoops"], ["willies", "moogs", "rifles"], 
     ["pools", "fawns", "tridents"]]
 >> products / 2
 => [["cycles", "vents"], ["hoops", "willies"], 
     ["moogs", "rifles"], ["pools", "fawns"], ["tridents"]]

The other alternative being a hopscotch with a flip-flop.

said on 27 Jan 2006 at 14:24

This should be faster (didn’t try it):


require 'enumerator'
class Array
  def /(len); enum_for(:each_slice,len).to_a end 
end

Actually it’s not Array.partition but Enumerable#partition, so you could define it there (given a better name).

said on 27 Jan 2006 at 15:04
class Array def / len each_index{|i| self[(i + 1)...(i + len)] = self[i+len] } end end
said on 27 Jan 2006 at 17:42

Is this the same as each_slice ?

Granted, the source isn’t quite as compact.

each_slice(step=nil, &yld) Iterate through slices. If slicing step is not given, the the arity if the block is used.
  
  x = []
  [1,2,3,4].each_slice(2){ |a,b| x << [a,b] }
  x  #=> [ [1,2], [3,4] ]

  x = []
  [1,2,3,4,5,6].each_slice(3){ |a| x << a }
  x  #=> [ [1,2,3], [4,5,6] ]

said on 27 Jan 2006 at 17:53

yxhuvud: Super sly, but that method needs a bang.

scili: Ohhh, using the arity of the block is a wise idea! Geez, Facets is an entire schoolhouse of fledgling RCRs.

said on 27 Jan 2006 at 21:00

each_slice is writen in a peculairly inefficient way, unless I’m mistaken, also… am I crazy or does the first example need to drop the parameter 2 to demonstrate the behavior when step isn’t supplied?

My fixed(?) version:
  def each_slice(step=nil, &yld)
    cnt = step || yld.arity.abs
    n = length / cnt
    n += 1 if length % cnt > 0
    n.times do |i|
      yld.call(*self.slice(i*cnt,cnt))
    end
    return cnt
  end

said on 27 Jan 2006 at 21:03

Uh, scratch that, not inefficient, just very unDRY.

said on 27 Jan 2006 at 22:00

ruby nube, but wouldn’t

class Array
  def / len
    inject([[]]) do |ary, x|
      ary << [] if ary.last.size >= self.size / len 
      ary.last << x
      ary
    end
  end
end

be more keeping with the idea of division?

said on 28 Jan 2006 at 02:00

Aye, the functionality described in the post is useful, but it’s not what I’d expect from what looks like a division operator. I’d expect

products / 2

to return

[["cycles", "vents", "hoops", "willies", "moogs"], ["rifles", "pools", "fawns", "tridents"]]

said on 28 Jan 2006 at 09:12

Maybe it should be called slice_by then?

said on 28 Jan 2006 at 10:56

whichever the algo, or name. just thought that it should be noted that its important to be keeping those nils around. e.g. size() instead of nitems()

[1,2,nil,3,nil,nil,4,5,6] / 4 => [[1, 2, nil, 3], [nil, nil, 4, 5], [6]] => [[1, 2, 3, 4], [5, 6]]

/ 4
said on 28 Jan 2006 at 11:00

oops. the examples was,

[1,2,nil,3,nil,nil,4,5,6] / 4 => [[1, 2, nil, 3], [nil, nil, 4, 5], [6]] <br><br> [1,2,nil,3,nil,nil,4,5,6].compact / 4 => [[1, 2, 3, 4], [5, 6]]
said on 28 Jan 2006 at 11:13

Okay, so maybe mine should use the modulo then.

said on 28 Jan 2006 at 11:59

i wrote some related but different code to cut up Ranges in to partitions of a known size: I called it Range.%

I think must have been half-asleep when I wrote the code, though, I looks sort of ugly:

class Range
  def %(q)
    q = 1 if q <= 0
    a = []

    i = self.end

    if(i == self.begin)
      return([self])
    end

    while(i > self.begin)
      lasti = i
      i -= q
    q = 1 if q.zero?
    a = []

    i = self.begin
    l = self.end
    l -= 1 if self.exclude_end?
    while(i <= self.end)
      lasti = i
      i += q
  if(l < i)
        if self.exclude_end?
          a.push(lasti...l)
        else
          a.push(lasti..l)
        end
      else
        a.push(lasti...i)
      end
    end
    return a
  end
end
irb(main):008:0> (1...100) % 10
=> [90...100, 80..90, 70..80, 60..70,
50..60, 40..50, 30..40, 20..30, 10..20,
1..10]
said on 28 Jan 2006 at 12:04

Actually, Why, I would call your method % and have / divide your array into (n) parts of equal size

[1,2,3,4,5,6] / 2 => [[1,2,3], [4,5,6]] [1,2,3,4,5,6] % 2 => [[1,2], [3,4], [5,6]]
said on 28 Jan 2006 at 12:05

nevermind that last comment, you’re already agreeing with me.

said on 28 Jan 2006 at 17:43
.oO(Why are they all rewriting what enumerator already does and more efficiently at that)
said on 28 Jan 2006 at 20:48

mfp: mmmmhm! hallelujah!(sic)

said on 29 Jan 2006 at 03:02

I’ve been lurking in the Ruby/Rails commuity for a while, but I haven’t seen many references to ‘Facets’. Why? Is seems a suprememly useful library, but no one mentions it.

For example; I don’t find it pre-gemed (real noob here) on many sites that offer Ruby/Rails hosting.

Will I run into trouble if I install then depend on Facets?

Sorry for the distraction.

said on 29 Jan 2006 at 03:46

n00bi3 – u could either ask them to add it for u (possibly offer them a nice chocolate cake as a reward) – they are usually quite friendly

or u could copy and paste the function(s) u need from the source code of the gem into ur project :-)

said on 29 Jan 2006 at 05:04

My two cents: a = (1..10).to_a; maxsubarraysize = 3-1; result = []; while a.size > 0; result << a.slice!(0..maxsubarraysize); end

said on 30 Jan 2006 at 03:08

”.oO(Why are they all rewriting what enumerator already does and more efficiently at that)”

shrug

Because we are ignorant.

cheers, prat

said on 30 Jan 2006 at 14:45

noobish: You don’t see many references to Facets because Facets is … far too large for most people to want to use. There are sometimes other reasons, too.

I personally wouldn’t use any library that depended on Facets, because I vehemently disagree with most of the “design” decisions made in making the library.

said on 30 Jan 2006 at 17:08
What about something like this?
# ---------------------------------------------------------------------------
# collect_every(n [,fill=false[,offset=0]])                  => an array
# collect_every(n [,fill=false[,offset=0]]) {|item| block}   => an_array
# ---------------------------------------------------------------------------
# If a block is given, it invokes the block passing in an array of n elements.
# The last array passed may not contain n elements if size % 2 does not equal
# zero. If no block is given, it returns an array containing the collections.
#
# If the optional argument fill is set to true, the empty spaces will be
# filled with nils. The optional argument offset allows the collection to 
# start at that index in the array.
#
# a = (1..10).to_a
# a.collect_every(5)               #=> [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]
# a.collect_every(5) {|x| p x}     #=> [1, 2, 3, 4, 5]
#                                      [6, 7, 8, 9, 10]
# b = (1..7).to_a
# b.collect_every(3)               #=> [[1, 2, 3], [4, 5, 6], [7]]
# b.collect_every(3,true)          #=> [[1, 2, 3], [4, 5, 6], [7,nil,nil]]
# b.collect_every(3,true,1)        #=> [[2, 3, 4], [5, 6, 7]]

class Array
  def collect_every(n,fill=false,offset=0)

    if block_given?
      while offset < size
        ret=[]

        if fill
          n.times do |x| 
            if offset+x > size - 1: ret << nil
            else ret << self[offset+x] end
          end
        else
          n.times { |x| ret << self[offset+x] unless offset+x > size-1 }
        end

        offset += n
        yield ret
        ret = nil
      end

    else
      ret = []
      while offset < size
        ret << []

        if fill
          n.times do |x|
            if offset+x > size - 1: ret.last << nil
            else ret.last << self[offset+x] end
          end
        else
          n.times { |x| ret.last << self[offset+x] unless offset+x > size-1 }
        end

        offset += n
      end
      return ret
    end

  end
end
said on 30 Jan 2006 at 17:33

Thanks JakDak, I know how to use gems. :-). I’m more interested in Austins answer. What design decisions did they make that turn you off?

said on 31 Jan 2006 at 02:16

Ezra, this is the all-in-one solution! Nice!

said on 31 Jan 2006 at 14:35

Why not “if offset+x > size – 1 THEN ret.last << nil; else ret.last … end”? Would be a bit easier to read, imho!

Comments are closed for this entry.