-1

Add a method each_run to the Ruby Array class which takes a code block expecting two arguments. It calls the code block once for each contiguous run of equal items in the array, sending the length of the run and the item repeated.the output will appear like this:

irb(main):001:0> load("eachrun.rb")    
=> true    
irb(main):002:0> [5,9,3,4,4,4,7,8,8].each_run { |n,x|    
irb(main):003:1*    print x, " appearing ", n, " times\n" }    
5 appearing 1 times    
9 appearing 1 times    
3 appearing 1 times    
4 appearing 3 times    
7 appearing 1 times    
8 appearing 2 times    
=> nil
1
  • 1
    nice, good luck in doing this Commented Mar 19, 2016 at 23:27

4 Answers 4

2

I would use group_by and yield the keys and size of the associated groups.

def each_run
  group_by(&:itself).map {|k,v| yield v.length, k }
end

I wouldn't monkey-patch it directly into Array, though. First, it would make more sense as part of Enumerable, and second, monkey-patching can get messy. You could make it a refinement, though. Sadly, you can only refine classes, not modules, so it's back to Array instead of Enumerable:

module EachRun 
  refine Array do 
    def each_run 
      group_by(&:itself).map {|k,v| yield v.length, k }
    end
  end
end

Then just put using EachRun at the top of any code that you want to be able to use your new method.

Sign up to request clarification or add additional context in comments.

Comments

1

I read the intention of the problem "It calls the code block once for each contiguous run of equal items in the array" as meaning the example list is a touch misleading since it contains no subsequent runs of a digit so that you can ignore the structure of the input list and just count how many times any given item appears. If the example had been:

[5,9,3,4,4,4,7,8,8,5,5]

Then printing "5 appears 3 times" doesn't suggest itself as correct.

I haven't written any Ruby in quite a while so this may not be optimal but it's what I came up with:

require 'test/unit/assertions'
include Test::Unit::Assertions

module Enumerable

  def runs
    if empty?
      []
    else
      runs = [[first,1]]
      self[1..-1].each do |item|
        if item == runs.last.first
          runs = runs[0..-2] + [[item,runs.last.last+1]]
        else
          runs = runs + [[item,1]]
        end
      end
      runs
    end
  end

  def each_run(&block)
    runs.each(&block)
  end
end

assert_equal( [], [].runs )
assert_equal( [[5,1],[9,1],[3,1],[4,3],[7,1],[8,2],[5,2]], [5,9,3,4,4,4,7,8,8,5,5].runs )

[5,9,3,4,4,4,7,8,8,5,5].each_run do |m,n|
  puts "#{m} appears #{n} times"
end

Outputting

> 5 appears 1 times
> 9 appears 1 times
> 3 appears 1 times
> 4 appears 3 times
> 7 appears 1 times
> 8 appears 2 times
> 5 appears 2 times

When I thought about it in Clojure what I came up with was this:

(defn runs [l]
  (reduce (fn [memo [x n :as e]]
            (let [[last-x last-n] (last memo)]
              (if (= x last-x)
                (conj (vec (drop-last memo)) [x (inc last-n)])
                (conj memo e))))
          [] (map (fn [e] [e 1]) l)))

(runs [5 5 9 8 1 1 4 3 3 3 3 5 5 5 2 2])
=> [[5 2] [9 1] [8 1] [1 2] [4 1] [3 4] [5 3] [2 2]]

Comments

0

This may give you a few ideas about how to proceed:

irb(main):001:0> a = [5,9,3,4,4,4,7,8,8]
=> [5, 9, 3, 4, 4, 4, 7, 8, 8]
irb(main):002:0> a.uniq.each { |e| printf("%d appears %d times\n", e, a.count(e)) }
5 appears 1 times
9 appears 1 times
3 appears 1 times
4 appears 3 times
7 appears 1 times
8 appears 2 times

Comments

0

I went with the suggest of using uniq and each but that's only if you need to return the uniq values as an array over all. There's lots of ways you can iterate it's up to you but the key here is monkey patching and yielding your values in order to use a block.


class Array
  def each_run
    uniq.each do |item|
      yield(item, self.count(item))
    end
  end
end

[12] pry(main)> [5,9,3,4,4,4,7,8,8].each_run do |num, count|
[12] pry(main)*   printf("%d appears %d times\n", num, count)
[12] pry(main)* end  
5 appears 1 times
9 appears 1 times
3 appears 1 times
4 appears 3 times
7 appears 1 times
8 appears 2 times
=> [5, 9, 3, 4, 7, 8]

sources:

Monkey Patch Array Stack Overflow

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.