如何在 Ruby 中用两个不同的分隔符拆分字符串并将其转换为两个数组?

How do I split a string with two different delimiters in Ruby and convert it to two arrays?

我有一个字符串

"Param 1: 1, Some text: 2, Example3: 3, Example4: 4"

我想将其转换成数组:

["Param 1","Some text","Example3","Example4"]
[1,2,3,4]

怎么办?

输入

a = "Param 1: 1, Some text: 2, Example3: 3, Example4: 4"

代码

obj= a.split(',').map { |x| x.split(':') }    
p obj.map(&:first).map(&:strip)    
p obj.map(&:last)

结果

["Param 1", "Some text", "Example3", "Example4"]
[" 1", " 2", " 3", " 4"]

在 Ruby 中有 很多 的方法。

您可以先按对的分隔符(逗号)拆分,然后使用 map 按 key/pair 分隔符(冒号)进一步拆分:

pairs = s.split(/,\s*/).map { |s| s.split(/:\s*/) }
keys = pairs.map(&:first)
values = pairs.map(&:last)

(此处和下方 s 是您的原始字符串)

您可以在一次调用中使用 scan 来匹配 keys/values(免责声明:这并不意味着这更有效——正则表达式并不神奇)

pairs = s.scan(/(?<key>[^:]+):\s*(?<value>[^,]+)[,\s]*/)
keys = pairs.map(&:first)
values = pairs.map(&:last)

(这里不需要命名捕获 - scan 它们不会带来任何好处 - 但我把它们放在一起可以使正则表达式更具可读性)

您可以按所有分隔符拆分,然后使用 Enumerable#partition 将键与值分开,smth。喜欢:

keys, values = s.split(/:\s*|,\s*/).partition.with_index { |_, i| i.even? }

等...

您可以将 String#split 与正则表达式一起使用,然后将返回值配对并转置:

str = "Param 1: 1, Some text: 2, Example3: 3, Example4: 4"
str.split(/[:,] +/).each_slice(2).to_a.transpose
  #=> [["Param 1", "Some text", "Example3", "Example4"],
  #    ["1", "2", "3", "4"]]

步骤如下

a = str.split(/[:,] +/)
  #=> ["Param 1", "1", "Some text", "2", "Example3", "3", "Example4", "4"]

正则表达式为“匹配一个冒号或逗号后跟一个或多个空格”。

enum = a.each_slice(2)
  #=> #<Enumerator: ["Param 1", "1", "Some text", "2", "Example3",
  #                  "3", "Example4", "4"]:each_slice(2)>
b = enum.to_a
  #=> [["Param 1", "1"], ["Some text", "2"], ["Example3", "3"],
  #    ["Example4", "4"]]
b.transpose
  #=> [["Param 1", "Some text", "Example3", "Example4"],
  #    ["1", "2", "3", "4"]]

这是我提供的第二种计算方法,没有解释,只是说它使用 String#gsub that takes one argument and no block, returning an enumerator that can be chained to Enumerator#with_object 的形式。 gsub 的这种形式与其他形式不同,它不执行字符替换(因此可能被认为命名不当)。相反,枚举器生成并 returns 匹配其参数,此处为正则表达式。

str.gsub(/[a-z][a-z ]+\d*(?=:)|\d+(?![^,])/i).with_object([[],[]]) do |s,(a,b)|
 (s[0].match?(/\d/) ? b : a) << s
end
  #=> [["Param 1", "Some text", "Example3", "Example4"],
  #    ["1", "2", "3", "4"]]

这是使用 Multiple Assignment 创建 2 个新命名数组的单行代码:

a1, a2 = str.split(", ").map{|x| x.split(": ")}.transpose

a1 #=>  ["Param 1", "Some text", "Example3", "Example4"]
a2 #=>  ["1", "2", "3", "4"]

尽管在之前的建议中已经以一种或另一种形式提到了大部分解决方案,但我更喜欢这种组合方法而不是提到的其他方法,原因如下:

  1. 它不使用正则表达式,因此比其他一些建议执行得更快。实际上,在 运行 一些快速基准测试之后,它看起来实际上比目前列出的其他非正则表达式解决方案执行得更快。
  2. 它同时创建了 2 个唯一且单独命名的数组以供使用(而不是仅创建一个包含 2 个子数组的数组)。
  3. 一行完成全部操作