2009-11-30 60 views
18

我试图使用Ruby将索引返回到字符串中特定字符的所有匹配项。示例字符串是"a#asg#sdfg#d##",当搜索#字符时预期回报为[1,5,10,12,13]。下面的代码可以完成这项工作,但是必须有一个更简单的方法来做到这一点?返回ruby中字符串中所有字符的索引

def occurances (line) 

    index = 0 
    all_index = [] 

    line.each_byte do |x| 
    if x == '#'[0] then 
     all_index << index 
    end 
    index += 1 
    end 

    all_index 
end 

回答

15
s = "a#asg#sdfg#d##" 
a = (0 ... s.length).find_all { |i| s[i,1] == '#' } 
+3

s =“a#asg#sdfg#d ##” a =(0 ... s.length).find_all {| i | s [i] =='#'}应该工作得对吗?不需要,1 ...? –

+0

@SamJoseph在这种情况下,是的,两者是同义词。 '[x,y]'的2个参数版本意味着“从'x'开始的长度为'y'的子字符串,它与'[x]'相同,这意味着”字符在'x'(也是一个字符串,因为红宝石没有Char类型)“。 – erich2k8

15
require 'enumerator' # Needed in 1.8.6 only 
"1#3#a#".enum_for(:scan,/#/).map { Regexp.last_match.begin(0) } 
#=> [1, 3, 5] 

ETA:这是通过创建一个使用scan(/#/)为每个方法的枚举。

扫描会产生指定模式(在本例中为/#/)的每次出现,并且在块内您可以调用Regexp.last_match来访问匹配的MatchData对象。

MatchData#begin(0)返回匹配开始处的索引,并且由于我们在枚举数上使用了map,我们得到了这些索引的数组。

+1

酷,但我不知道这是如何工作的。 – Gerhard

2

这是一个漫长的方法链:

"a#asg#sdfg#d##". 
    each_char. 
    each_with_index. 
    inject([]) do |indices, (char, idx)| 
    indices << idx if char == "#" 
    indices 
    end 

# => [1, 5, 10, 12, 13] 

需要1.8.7+

+0

在1.9中,您可以执行'.each_char.with_index'(而不是'each_char.each_with_index')。我认为,它读得更好。 – Telemachus

+0

确实如此。 –

12

这里有一个花哨更低的方式:

i = -1 
all = [] 
while i = x.index('#',i+1) 
    all << i 
end 
all 

在快速的速度测试,这是大约比FM的find_all方法快3.3倍,比sepp2k的enum_for方法快大约2.5倍。

+0

那些速度数字是从1.8.5。在1.9.1中,这仍然是最快的,但find_all大约慢了3倍,而enum_for大约慢了5倍! –

+0

我的猜测是它是'Regexp.last_match.begin(0)',它正在减慢'enum_for'方法的速度。 (也就是说,我希望'enum_for'本身不是问题。)无论哪种方式,我都喜欢它,它既简单又可读。不那么花俏通常更好。 – Telemachus

+0

这样做更快,因为其他方法中的每个字符都会执行一个块。我遇到并解决了类似的问题,在http://stackoverflow.com/questions/6387428/why-is-counting-letters-faster-using-stringcount-than-using-stringchars-in-ruby/6475413#6475413 –

1

从FMC的回答得到的另一个解决方案:

s = "a#asg#sdfg#d##" 
q = [] 
s.length.times {|i| q << i if s[i,1] == '#'} 

我喜欢红宝石从来没有做的事情只有一个办法!

相关问题