comparing sequences in Ruby

假装没事ソ 提交于 2019-12-07 06:13:37

问题


Assuming I have to (small-to-medium) arrays:

tokens = ["aaa", "ccc", "xxx", "bbb", "ccc", "yyy", "zzz"]
template = ["aaa", "bbb", "ccc"]

How can I determine whether tokens contains all entries of template, in that same order?

(Note that in the example above, the first "ccc" should be ignored, resulting in a match due to the last "ccc".)


回答1:


Cleanest, I think, to do this via recursion:

class Array
  def align(other)
    if pos = index(other.first)
      other.size == 1 || slice(pos..-1).align(other.drop(1))
    end
  end
end

so:

[1,2,3,4,3,2,1].align([1,2,3])
=> true
[1,2,3,4,3,2,1].align([1,4,1])
=> true
[1,2,3,4,3,2,1].align([1,4,2,3])
=> nil



回答2:


This works for your sample data.

tokens = ["aaa", "ccc", "xxx", "bbb", "ccc", "yyy", "zzz"]
template = ["aaa", "bbb", "ccc"]

pos = 0
condition_met = true
template.each do |temp|
  if (tpos = tokens[pos..-1].index temp) == nil then
    break condition_met = false
  else
    pos = tpos
  end
end

puts condition_met



回答3:


Solution provided by manatwork is good, but here is one that seems more ruby-ish to me:

tokens = ["aaa", "ccc", "xxx", "bbb", "ccc", "yyy", "zzz"]
template = ["aaa", "bbb", "ccc"]

def tokens_include_template(tokens, template)
  tokens = tokens.to_enum
  template.each do |t|
    return false unless loop { break true if t == tokens.next }
  end
  true
end

puts tokens_include_template(tokens, template)



回答4:


This is a one-liner condition:

 tokens.select {|t| t if template.include?(t)}.reverse.uniq == template.reverse \
  or \
   tokens.select {|t| t if template.include?(t)}.uniq == template

Example:

def check_order(tokens, template)
   tokens.select {|t| t if template.include?(t)}.reverse.uniq == template.reverse \
    or \
     tokens.select {|t| t if template.include?(t)}.uniq == template
end

tokens = ["aaa", "xxx", "bbb", "ccc", "yyy", "zzz"]
template = ["bbb", "aaa", "ccc"]
check_order(tokens,template) # => false

tokens = ["aaa", "ccc", "xxx", "bbb", "ccc", "yyy", "zzz"]
template = ["aaa", "bbb", "ccc"]
check_order(tokens,template) # => true

tokens = ["aaa", "ccc", "xxx", "bbb", "ccc", "yyy", "zzz"]
template = ["aaa", "ccc", "bbb"]
check_order(tokens,template) # => true



回答5:


Here's another idea, if the arrays are small-to medium, it might work fine. It just converts the tokens into a regexp and tries to match the template against it. (This will also treat empty template as if it matches tokens, so if you don't want that, just handle this corner case explicitly)

def tokens_in_template? tokens, *template
  re = /^#{tokens.map {|x| "(?:#{x})?"}.join}$/
  !! (template.join =~ re)
end

tokens = ["aaa", "ccc", "xxx", "bbb", "ccc", "yyy", "zzz"]
puts tokens_in_template? tokens                            # => true
puts tokens_in_template? tokens, "aaa", "bbb", "ccc"       # => true
puts tokens_in_template? tokens, "aaa", "bbb", "ccc", "aa" # => false
puts tokens_in_template? tokens, "aaa", "zzz", "ccc"       # => false
puts tokens_in_template? tokens, "aaa", "zzz"              # => true



回答6:


Just subtract the first array from the second array if the result is empty you have your match

result = template - tokens
if result.empty?
  #You have a match
else
  #No match
end

Read more about arrays here http://www.ruby-doc.org/core/classes/Array.html#M000273

If order is important then use <=> operator again described in the link above



来源:https://stackoverflow.com/questions/6993848/comparing-sequences-in-ruby

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!