1

For example,

input = ['a','b','b','b','a','a','b','c','c']

output = ['a',['b','b','b'],['a','a'],'b',['c','c']]

Here, if there are more than one same elements then those are put into sub-array.

Can you please help me out?

Zong
  • 6,160
  • 5
  • 32
  • 46
  • 3
    Ruby 1.8.7 is *ancient*. And it is no longer supported. You tagged your question with [tag:ruby-on-rails] and [tag:ruby-1.8.7], which makes me very nervous. Are you *really* running a website off a version of Ruby that has been unsupported for almost a year, and does not receive any security fixes? Note that after support for Ruby 1.8.7 ended, at least one serious vulnerability has been found: [CVE-2013-4164: *Heap-based buffer overflow in Ruby 1.8 […] allows context-dependent attackers to […] possibly execute arbitrary code […]*](http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2013-4164) – Jörg W Mittag May 09 '14 at 12:44
  • Homework question? What have you tried? What was the result? – Pavling May 09 '14 at 13:11
  • @Pavling Humm. Now-a-days we provide answers to homework questions also in SO. No problem here... :-) – Arup Rakshit May 09 '14 at 13:38
  • @JörgWMittag: although I understand your anger (in the company where I am the CTO I upgrade Ruby as soon as possible), there are some contexts where Ruby 1.8.7 is acceptable; for example on a Debian server, which provides a fully patched version of Ruby 1.8.7 – mdesantis May 09 '14 at 13:43
  • @ArupRakshit you might, but I won't (seeing as I set questions as homework... it would be a bit pointless to answer them too) - and since it's a community, we're both free to follow our own preferences. There are discussions about this: http://meta.stackexchange.com/questions/10811/how-do-i-ask-and-answer-homework-questions – Pavling May 09 '14 at 18:25
  • @Pavling What you are saying true.. I meant to say, we are not following, but we should.. I am with you.. :-) – Arup Rakshit May 09 '14 at 18:28

3 Answers3

4

This solution uses a sneaky trick to prevent having to check if a list element is either a single element or an Array already. By doing [*list[-1]] we make sure to always end up with an Array, regardless of whether the last element in the list we are building was a single element (e.g., 'a') or an Array already (e.g., ['b','b']). Then, we check if the first element of that Array (could be any element, they are all the same) is equal to the element of input we are currently processing. If so, we append it to the created Array. Otherwise, we just append it to list since it is different from the previous element.

input = ['a','b','b','b','a','a','b','c','c']

output = input.reduce([]) do |list, e|
  last = [*list[-1]]
  if last[0] == e
    list[-1] = last << e
  else
    list << e
  end
  list
end

p output
# ["a", ["b", "b", "b"], ["a", "a"], "b", ["c", "c"]]

Alternatively, with checking specifically for the type of the element it would be something like this:

output = input.reduce([]) do |list, e|
  last = list.last
  if last.is_a? Array and last.include? e
    last << e
  elsif last == e
    list[-1] = [last, e]
  else
    list << e
  end
  list
end

This was tested on Ruby 2.0, but it should run fine on 1.8.7 as well.

Daniël Knippers
  • 3,049
  • 1
  • 11
  • 17
  • I like [*my_arr[i]]. It is sadly not obvious to engineers disinterested in style-isms – New Alexandria May 09 '14 at 13:22
  • @NewAlexandria Fair point, although they can just use the alternative implementation where it's very obvious what's going on :) I will change that one slightly to improve readability a bit more. – Daniël Knippers May 09 '14 at 13:31
  • oh, no!, I like the concise form. I just wanted to draw attention to the care needed when implementing it – New Alexandria May 09 '14 at 14:18
4

Ruby 1.9+ has Enumerable#chunk, which will almost do what you want:

input.
  chunk {|e| e }.
  map(&:last).
  map {|e| if e.size == 1 then e.first else e end }

For older versions of Ruby, Enumerable#chunk is available in Marc-André Lafortunes backports gem. Just add

require 'backports/1.9.2/enumerable/chunk'

on top.

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • 2
    Gotta love Ruby for all sorts of built-ins. Although this solution has to iterate the whole input 3 times as opposed to once (with `chunk`, `map`, `map`). Then again, scalability is not really a concern here :) – Daniël Knippers May 09 '14 at 13:39
  • I would implement this style more, for readability. I have seen otherwise-smart engineers at big firms cry when having to look at things that are spelled-out any less than this. Pity, but the cultural norm of the trade – New Alexandria May 09 '14 at 14:20
0

I was under the impression that, this problem can be solved using slice_before. But couldn't figured out. Finally Robert Klemme has guided me in the proper direction. Hope you guys would also like it.

input = ['a','b','b','b','a','a','b','c','c']
prev = nil
input.slice_before { |e| (e != prev).tap { prev = e } }.
      map { |elem| elem.size == 1 ? elem.first : elem }
# => ["a", ["b", "b", "b"], ["a", "a"], "b", ["c", "c"]]
Arup Rakshit
  • 116,827
  • 30
  • 260
  • 317