All occurances of a character in a string

Hi - I am trying to figure out the ruby way of printing all occurances
of a character in a string…

The index method on string gives only the first occurance only.

Example input string - abbccdaab
desired output - ‘a’ = 0 6 7
‘b’ = 1 2 8
‘c’ = 3 4
‘d’ = 5

Any help greatly appreciated…

Raghu Go wrote:

Example input string - abbccdaab
desired output - ‘a’ = 0 6 7
‘b’ = 1 2 8
‘c’ = 3 4
‘d’ = 5

irb(main):001:0> a = “abbccdaab”
=> “abbccdaab”
irb(main):002:0> i = 0
=> 0
irb(main):003:0> ret = Hash.new
=> {}
irb(main):004:0> a.scan(/./) {|c|
irb(main):005:1* d = [i]
irb(main):006:1> ret[c] = ret[c].nil? ? d : d << ret[c]
irb(main):007:1> ret[c].flatten!
irb(main):008:1> i = i + 1
irb(main):009:1> }
=> “abbccdaab”
irb(main):010:0> p a
“abbccdaab”
=> nil
irb(main):011:0> p ret
{“a”=>[7, 6, 0], “b”=>[8, 2, 1], “c”=>[4, 3], “d”=>[5]}
=> nil
irb(main):012:0>

Hi Raghu

My 2-minute horrible-hack™:

input = “abbccdaab”
i = 0;
input.split(‘’).inject({}) {|a,v| a[v] = a[v] ? (a[v] <<
input[i…-1].index(v)+i) : [input[i…-1].index(v)+i]; i+=1; a}

{“a”=>[0, 6, 7], “b”=>[1, 2, 8], “c”=>[3, 4], “d”=>[5]}

I am sure there has to be a MUCH nicer way though, because this is
rather ugly…

Cheers,
Peter


http://www.rubyrailways.com
http://scrubyt.org

S2 wrote:

irb(main):006:1> Â ret[c] = ret[c].nil? ? d : d << ret[c]
irb(main):007:1> Â ret[c].flatten!

oh sorry, here are a few things that are not needed:
a = “abbccdaab”
i = 0
ret = Hash.new
a.scan(/./) {|c|
ret[c] = ret[c].nil? ? [i] : ret[c] << i
i = i + 1
}
p a
p ret

I did a quick test and like Siep’s modifications…

Method1 Time elapsed: 0.236999988555908 milliseconds

Siep’s modification Method2 Time elapsed: 0.123999834060669 milliseconds

def method1(a)
i = 0
ret = Hash.new
a.scan(/./) {|c|
ret[c] = ret[c].nil? ? [i] : ret[c] << i
i = i + 1
}
p a
p ret
end

def method2(str) #Siep
ar = str.split("")
result = Hash.new{Array.new}

ar.each_with_index do|item,index|
result[item] = result[item]<<index
end
result.each{|k,v| puts k + " = " + v.join(",")}
end

Siep K. wrote:

S2 wrote:

S2 wrote:

irb(main):006:1> ret[c] = ret[c].nil? ? d : d << ret[c]
irb(main):007:1> ret[c].flatten!

oh sorry, here are a few things that are not needed:
a = “abbccdaab”
i = 0
ret = Hash.new
a.scan(/./) {|c|
ret[c] = ret[c].nil? ? [i] : ret[c] << i
i = i + 1
}
p a
p ret

The block form of Hash.new gets rid of the if-statement; like this:

str = “abbccdaab”
ar = str.split("")
result = Hash.new{Array.new}

ar.each_with_index do|item,index|
result[item] = result[item]<<index
end

result.each{|k,v| puts k + " = " + v.join(",")}

regards,

Siep

S2 wrote:

S2 wrote:

irb(main):006:1> Â ret[c] = ret[c].nil? ? d : d << ret[c]
irb(main):007:1> Â ret[c].flatten!

oh sorry, here are a few things that are not needed:
a = “abbccdaab”
i = 0
ret = Hash.new
a.scan(/./) {|c|
ret[c] = ret[c].nil? ? [i] : ret[c] << i
i = i + 1
}
p a
p ret

The block form of Hash.new gets rid of the if-statement; like this:

str = “abbccdaab”
ar = str.split("")
result = Hash.new{Array.new}

ar.each_with_index do|item,index|
result[item] = result[item]<<index
end

result.each{|k,v| puts k + " = " + v.join(",")}

regards,

Siep

Hi –

On Sat, 26 Apr 2008, Raghu Go wrote:

Any help greatly appreciated…

I have this really strong feeling that there’s a really easy way, but
I can’t think of it or find it. Meanwhile, if you’re using 1.9 you
could do something like:

str = “abcdbcdae”

h = Hash.new {|h,k| h[k] = [] }
str.each_char.map.with_index {|char, i| h[char] << i }

which would put a hash of results in h.

I just have this hunch that there was a way to accumulate offsets from
a scan or gsub operation, but I can’t come up with it.

David

Raghu Go wrote:

I did a quick test and like Siep’s modifications…

Method1 Time elapsed: 0.236999988555908 milliseconds

Siep’s modification Method2 Time elapsed: 0.123999834060669 milliseconds

your computer must be really slow :slight_smile:

s2@magnesium:~/temp$ cat test.rb
def method1(a)
i = 0
ret = Hash.new
a.scan(/./) {|c|
ret[c] = ret[c].nil? ? [i] : ret[c] << i
i = i + 1
}
p a
p ret
end

def method2(str) #Siep
ar = str.split("")
result = Hash.new{Array.new}

ar.each_with_index do|item,index|
result[item] = result[item]<<index
end
result.each{|k,v| puts k + " = " + v.join(",")}
end

def method3(a)
result = Hash.new{Array.new}
a.split(’’).each_with_index {|c, i| result[c] = result[c] << i}
p result
end
s2@magnesium:~/temp$ irb
irb(main):001:0> require ‘benchmark’
=> true
irb(main):002:0> include Benchmark
=> Object
irb(main):003:0> require ‘test’
=> true
irb(main):004:0> puts Benchmark.measure {method1(“abbccdaab”)}
“abbccdaab”
{“a”=>[0, 6, 7], “b”=>[1, 2, 8], “c”=>[3, 4], “d”=>[5]}
0.000000 0.000000 0.000000 ( 0.000227)
=> nil
irb(main):005:0> puts Benchmark.measure {method2(“abbccdaab”)}
a = 0,6,7
b = 1,2,8
c = 3,4
d = 5
0.000000 0.000000 0.000000 ( 0.000278)
=> nil
irb(main):006:0> puts Benchmark.measure {method3(“abbccdaab”)}
{“a”=>[0, 6, 7], “b”=>[1, 2, 8], “c”=>[3, 4], “d”=>[5]}
0.000000 0.000000 0.000000 ( 0.000209)
=> nil
irb(main):007:0>

On 26.04.2008 16:53, David A. Black wrote:

desired output - ‘a’ = 0 6 7
str = “abcdbcdae”

h = Hash.new {|h,k| h[k] = [] }
str.each_char.map.with_index {|char, i| h[char] << i }

which would put a hash of results in h.

In 1.8

irb(main):001:0> require ‘enumerator’
=> true
irb(main):002:0> str = “abcdbcdae”
=> “abcdbcdae”
irb(main):003:0> ind = Hash.new {|h,k| h[k]=[]}
=> {}
irb(main):004:0> str.to_enum(:scan,/./).each_with_index {|c,i|ind[c]<<i}
=> #Enumerable::Enumerator:0x7ff75768
irb(main):005:0> ind
=> {“a”=>[0, 7], “b”=>[1, 4], “c”=>[2, 5], “d”=>[3, 6], “e”=>[8]}

I just have this hunch that there was a way to accumulate offsets from
a scan or gsub operation, but I can’t come up with it.

Like this?

irb(main):017:0> str = “abcdbcdae”
=> “abcdbcdae”
irb(main):018:0> ind = Hash.new {|h,k| h[k]=[]}
=> {}
irb(main):019:0> str.scan(/./) {|c| ind[c] << $`.length}
=> “abcdbcdae”
irb(main):020:0> ind
=> {“a”=>[0, 7], “b”=>[1, 4], “c”=>[2, 5], “d”=>[3, 6], “e”=>[8]}

Cheers

robert

Hi –

On Sun, 27 Apr 2008, Robert K. wrote:

Example input string - abbccdaab

=> true

a scan or gsub operation, but I can’t come up with it.
=> {“a”=>[0, 7], “b”=>[1, 4], “c”=>[2, 5], “d”=>[3, 6], “e”=>[8]}
It may well have been that, or something like it.

David

I apologise for the complete ugliness of this code :slight_smile:

Look away of you’re screamish!

ARGV.to_s.split(/\s*/).map{|x|x.downcase}.inject(Hash.new(0)){|h,x|h[x]
+=1 and h}.each{|i,h| print "#{i}(#{h}), "}

Foobar, output:

a(1), b(1), o(2), f(1), r(1)

Whoops, I didn’t read the question properly, this prints all
occurances, not positions…

On Sat, Apr 26, 2008 at 9:55 PM, Raghu Go [email protected] wrote:

Hi - I am trying to figure out the ruby way of printing all occurances
of a character in a string…

The index method on string gives only the first occurance only.

I thought of the ‘indexes’ method, but that works in reverse. So I
made my own version:

class Array
def all_indexes val
(0…length).map{|i|self[i]==val ? i : nil}.compact
end
alias :all_indicies :all_indexes
end

p ‘hello world’.split(‘’).all_indicies(‘l’)

=> [2, 3, 9]

Example input string - abbccdaab
desired output - ‘a’ = 0 6 7
‘b’ = 1 2 8
‘c’ = 3 4
‘d’ = 5

Any help greatly appreciated…

Source = ‘abbccdaab’
s=Source.split(‘’)
s.uniq.each{|v| puts “‘#{v}’ = #{s.all_indexes(v)*’ '}”}

=> ‘a’ = 0 6 7
‘b’ = 1 2 8
‘c’ = 3 4
‘d’ = 5

-Adam

On Sat, Apr 26, 2008 at 9:55 PM, Raghu Go [email protected] wrote:

Any help greatly appreciated…

Posted via http://www.ruby-forum.com/.

Here is one way.

arr = “abbccdaab”.split(//)
arr.uniq.each do |s|
res = []
(0…arr.length).each {|x| res << x if arr[x] =~ /#{s}/}
p s, res
end

Harry