我正在处理ruby中的文档。
我有一个文档,我正在使用regexp提取特定字符串,然后将它们添加到另一个文件中。当添加到目标文件时,它们必须是唯一的,因此如果目标文件中已存在该字符串,则添加一个简单的后缀,例如<word>_1
。最终我想按名称引用字符串,因此从日期开始随机数生成或字符串是不行的。
目前我正在存储添加到数组中的每个单词然后每次我添加一个单词我检查字符串不存在于数组中如果只有1个重复就可以了,但是可能有2个或更多,所以我需要检查初始字符串,然后循环递增后缀,直到它不存在,(我已经简化了我的代码,所以可能存在错误)
def add_word(word)
if @added_words include? word
suffix = 1
suffixed_word = word
while added_words include? suffixed_word
suffixed_word = word + "_" + suffix.to_s
suffix += 1
end
word = suffixed_word
end
@added_words << word
end
它看起来很乱,有没有更好的算法或ruby方式这样做?
答案 0 :(得分:2)
让@added_words
成为一个集合(不要忘记require 'set'
)。这样可以更快地查找,因为集合是使用哈希实现的,同时仍然使用include?
来检查集合成员资格。提取最高使用后缀也很容易:
>> s << 'foo'
#=> #<Set: {"foo"}>
>> s << 'foo_1'
#=> #<Set: {"foo", "foo_1"}>
>> word = 'foo'
#=> "foo"
>> s.max_by { |w| w =~ /#{word}_?(\d+)?/ ; $1 || '' }
#=> "foo_1"
>> s << 'foo_12' #=>
#<Set: {"foo", "foo_1", "foo_12"}>
>> s.max_by { |w| w =~ /#{word}_?(\d+)?/ ; $1 || '' }
#=> "foo_12"
现在要获得您可以插入的下一个值,您可以执行以下操作(假设您已经有12个foo
,所以下一个应该是foo_13
):
>> s << s.max_by { |w| w =~ /#{word}_?(\d+)?/ ; $1 || '' }.next
#=> #<Set: {"foo", "foo_1", "foo_12", "foo_13"}
对不起,如果这些例子有点困惑,我今天早些时候就麻醉了。它应该足以让您了解集合如何可能对您有所帮助(大多数集合也适用于数组,但集合可以更快地查找)。
答案 1 :(得分:1)
在这种情况下,我可能会使用一个集合或哈希:
#in your class:
require 'set'
require 'forwardable'
extend Forwardable #I'm just including this to keep your previous api
#elsewhere you're setting up your instance_var, it's probably [] at the moment
def initialize
@added_words = Set.new
end
#then instead of `def add_word(word); @added_words.add(word); end`:
def_delegator :added_words, :add_word, :add
#or just change whatever loop to use #@added_words.add('word') rather than self#add_word('word')
#@added_words.add('word') does nothing if 'word' already exists in the set.
如果你有一些通过这些部分进行分组的属性,那么散列可能会更好:
#elsewhere you're setting up your instance_var, it's probably [] at the moment
def initialize
@added_words = {}
end
def add_word(word, attrs={})
@added_words[word] ||= []
@added_words[word].push(attrs)
end
答案 2 :(得分:1)
将@added_words更改为哈希值,默认值为零。然后你可以这样做:
@added_words = Hash.new(0)
def add_word( word)
@added_words[word] += 1
end
# put it to work:
list = %w(test foo bar test bar bar)
names = list.map do |w|
"#{w}_#{add_word(w)}"
end
p @added_words
#=> {"test"=>2, "foo"=>1, "bar"=>3}
p names
#=>["test_1", "foo_1", "bar_1", "test_2", "bar_2", "bar_3"]
答案 3 :(得分:1)
以“错误的方式”进行,但代码更精确:
def add_word(word)
if @added_words.include? word
suffixed_word = 1.upto(1.0/0.0) do |suffix|
candidate = [word, suffix].join("_")
break candidate unless @added_words.include?(candidate)
end
word = suffixed_word
end
@added_words << word
end