在Ruby中,如何在符合条件的数组中合并连续的标记?

时间:2017-06-19 20:00:32

标签: arrays ruby merge

这是Ruby 2.4。我有一个字符串数组,如果它们以特殊字符开头或结尾,我想融合连续元素。所以,例如,如果我的特殊角色是“&”我有一个数组

["a &", "b", "c", "d"]

我希望结果是

["a & b", "c", "d"]

同样,如果数组是

["a", "&", "b", "c"]

我希望结果是

["a & b", "c"]

如果数组是

["a", "& b", "c"]

我希望结果是

["a & b", "c"]

但是,如果数组是

["&", "b", "c"]

结果应为

["&", "b", "c"]

因为在我的特殊字符之前没有非特殊元素。所以我尝试了这个解决方案

2.4.0 :012 >   words = ["a", "&", "b", "d"]
 => ["a", "&", "b", "d"]
2.4.0 :013 > SPECIAL_TOKENS = %w(&).freeze
 => ["&"]
2.4.0 :014 > words = words.chunk_while { |i, _| i.end_with?(*SPECIAL_TOKENS) }.map(&:join)
 => ["a", "&b", "d"]

但正如你所看到的那样,它并没有将第一个元素与其余元素融合在一起(我也失去了“&”和“b”之间的空格)。我是否可以对上述内容进行调整以使其按预期工作?

3 个答案:

答案 0 :(得分:1)

这有两种方法可以做到这一点。我假设,在示例中,没有数组的元素(字符串)以空格开头或结尾。

<强>#1

def join_some(arr, join_ch='&')
  arr.drop(1).each_with_object([arr.first]).with_index(1) do |(s,a),i|
    if (s[0] == join_ch && (i < arr.size - 1 || s.size > 1)) || 
       (a.last[-1] == join_ch && (i > 1 || a.last.size > 1)) 
      a.last << " #{s}"
    else
      a << s
    end
  end
end

join_some ["a", "&", "b", "d"]      #=> ["a & b", "d"]
join_some ["a", "& b", "c"]         #=> ["a & b", "c"]
join_some ["&", "b", "c"]           #=> ["&", "b", "c"]
join_some ["a", "b", "&"]           #=> ["a", "b", "&"]
join_some ["a", "&", "b", "&", "c"] #=> ["a & b & c"]
join_some ["a", "& b &", "c"]       #=> ["a & b & c"]
join_some ["&", "& b", "c"]         #=> ["& & b", "c"]
join_some [" &", "b", "c"]          #=> [" & b", "c"]
join_some ["&", "&", "&"]           #=> ["& & &"]
join_some ["a", "+ b", "+ c"], "+"  #=> ["a + b + c"]

我不确定前几个例子的返回值是否符合要求。

<强>#2

第一步是join(c)数组的所有元素形成一个单独的字符串,其中c是一个字符,不存在于数组的任何元素中。 (我选择使用0.chr #=> x00。)然后我们可以根据需要使用正则表达式rsplit(r)字符串。

def join_some(arr, join_ch='&')
  split_ch = 0.chr
  r = /(?<=\A.|[^#{join_ch}])#{split_ch}(?=[^#{join_ch}]|.\z)/
  arr.join(split_ch).split(r).map { |s| s.tr(split_ch, ' ') }
end

该方法给出与上面给出的10个例子的第一种方法相同的结果。正则表达式在自由间距模式下表示,带有解释性注释。这里join_ch #=> "&"split_ch #=> \x00

r = /
    (?<=            # begin positive lookbehind
      \A            # match the beginning of the string
      .             # match any char
      |             # or
      [^#{join_ch}] # match any char other than the join char
    )               # end positive lookbehind
    #{split_ch}     # match split char
    (?=             # begin a positive lookahead
      [^#{join_ch}] # match any char other than the join char
      |             # or
      .             # match any char
      \z            # match end of the string
    )               # end positive lookahead
    /x              # free-spacing regex definition mode
  #=> (?<=          # begin positive lookbehind
  #     \A          # match the beginning of the string
  #     .           # match any char
  #     |           # or
  #     [^&]        # match any char other than the join char
  #   )             # end positive lookbehind
  #   \x00          # match split char
  #   (?=           # begin a positive lookahead
  #     [^&]        # match any char other than the join char
  #     |           # or
  #     .           # match any char
  #     \z          # match end of the string
  #   )             # end positive lookahead
  #   /x            # free-spacing regex definition mode

答案 1 :(得分:0)

这很啰嗦,但通过了测试。该方法采用数组和特殊字符作为参数。最初的join.split.reject.map'将数组标准化为剥离的单个元素,除了两边都有空格的特殊字符。然后我们两次申请Enumerable#chunk_while

def chunky arr, char
  char = "\\".concat char
  arr.join(' ')
     .split(/(\w)/)
     .reject { |s| s.strip.empty? }
     .map { |e| e.match?(' &') ? ' & ': e }
     .chunk_while { |a,b| a.match?(/\s#{char}\s/) && b.match?(/\w/) }
     .map(&:join)
     .chunk_while { |a,b| a.match?(/\w/) && b.match?(/\s#{char}\s\w/) }
     .map { |s| s.join.strip }
end

p chunky ["a &", "b", "c", "d"], '&'
 #=> ["a & b", "c", "d"]
p chunky ["a", "-", "b", "c"], '-'
 #=> ["a - b", "c"]
p chunky ["a", "+ b", "c"], '+'
 #=> ["a + b", "c"]
p chunky ["*", "b", "c"], '*'
 #=> ["*", "b", "c"]
p chunky ["a", "b", "&"], '&'
 #=> ["a", "b", "&"]
p chunky ["a", "b", "& c"], '&'
 #=> ["a", "b & c"]

答案 2 :(得分:0)

我的解决方案并没有使用特殊字符,在我看来,这是最不令人惊讶的,它

  • 忽略字符串开头或结尾的额外尾随
  • 单独留下(单独的)&amp; s(也许你想在它们之间插入一些东西)
  • 适用于&amp;&amp;,&amp;&amp;&amp;,&amp; hello&amp;,...

代码:

-(void)tapDetected{
    HomeMainViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier: @ "homeVC"];
    [self.navigationController pushViewController:vc animated: YES];
}

结果:

def join_strings array, s = '&'
  array = array.flat_map {|i| i.match(Regexp.new"(?:^ *(?:\\#{s} )*(\\#{s})(?:\\#{s} )* *$)|(?:^ *(?:(\\#{s}) )*(.*?)(?: (\\#{s}))* *$)")[1..4]} - [nil]
  i = 0
  while i < array.length - 2
    if array[i..i+2].map {|i| i == s} == [false, true, false]
      array[i] = array[i..i+2].join ' '
      array.slice!(i + 1, 2)
    else i += 1 end
  end
  array
end