Ruby 匹配第一个数组和第二个数组中的元素
Ruby match elements from the first with second array
我有两个数组。第一个将是一个包含名称和数量的字符串数组。第二个是字母数组。
a1 = ["ASLK 50", "BSKD 150", "ZZZZ 100", "BSDF 50"]
a2 = ["B", "Z"]
我想创建第三个数组来根据 a2 对 a1 中的内容进行排序,return 根据第一个数组中的信息对数字进行排序。由于 a2 有 "B" 和 "Z",我需要扫描第一个数组以查找以字母 B 和 Z 开头的所有条目并将数字相加。
我的最终目标是 return 第三个数组的总和,像这样:
a3 = ["B = 200", "Z = 100"]
因为"A"不在a2上,所以不算。
我能够从 a1 中提取信息:
arr = a1.map{|el| el[0] + " : " + el.gsub(/\D/, '')}
#=> ["A : 50", "B : 150", "Z : 100", "B : 50"]
我无法比较 a1 和 a2。我尝试过不同的方法,例如:
a1.find_all{|i| i[0] == a2[0]} #=> only returns the first element of a2. How can I iterate through a2?
或者,
i = 0
arr_result = []
while i < (arr.length + 1)
#(append to arr_result the number from a1 if a1[i][0] matches a2[i])
我认为两者都可以解决它,但我不能将这两个想法都归结为工作代码。我怎样才能实施这两种方法?有没有更有效的方法呢?
我会这样做:
a1 = ["ASLK 50", "BSKD 150", "ZZZZ 100", "BSDF 50"]
a2 = ["B", "Z"]
a3 = a1.each_with_object(Hash.new(0)) do |a,obj|
obj[a[0]] += a.split.last.to_i if a2.include?(a[0])
end.map {|a| a.join(" = ")}
#=>["B = 200", "Z = 100"]
第一步通过将第二个数组中包含的每个第一个字母的值相加,将它们全部添加到 Hash
中。
第二步提供所需的输出
如果您想要一个 Hash,只需取消对 map 的最后一次调用即可。
{"B" => 200, "Z" => 100}
运行你的要求,就是你要转这个:
a1 = ["ASLK 50", "BSKD 150", "ZZZZ 100", "BSDF 50"]
a2 = ["B", "Z"]
进入这个:a3 = ["B = 200", "Z = 100"]
a3 = a2.map do |char|
sum = a1.reduce(0) do |sum, item|
name, price = item.split(" ")
sum += price.to_i if name[0].eql?(char)
sum
end
"#{char} = #{sum}"
end
与其将 Array
定义为 ["B = 200", "Z = 100"]
,不如将其定义为 映射 - 即以下 Hash
对象:
{"B" => 200, "Z" => 100}
至于实现,有很多方法可以实现。这只是一种方法:
a1 = ["ASLK 50", "BSKD 150", "ZZZZ 100", "BSDF 50"]
a2 = ["B", "Z"]
result = a2.map do |letter|
[
letter,
a1.select {|str| str[0] == letter}
.inject(0) {|sum, str| sum += str[/\d+/].to_i}
]
end.to_h
puts result # => {"B"=>200, "Z"=>100}
解释:
- 在顶层,我使用
Array#to_h
将数组对:[["B", 200], ["Z", 100]]
转换为 Hash
:{"B" => 200, "Z" => 100}
.
a1.select {|str| str[0] == letter}
仅选择 a1
中首字母为哈希键的元素。
inject(0) {|sum, str| sum += str[/\d+/].to_i}
将所有数字相加,安全防护默认为零(而不是 nil
意外抛出)。
这与@engineersmnky 的回答非常相似。
r = /
\A[A-Z] # match an upper case letter at the beginning of the string
| # or
\d+ # match one or more digits
/x # free-spacing regex definition mode
a1.each_with_object(Hash.new(0)) do |s,h|
start_letter, value = s.scan(r)
h[start_letter] += value.to_i if a2.include?(start_letter)
end.map { |k,v| "#{k} = #{v}" }
#=> ["B = 200", "Z = 100"]
Hash.new(0)
通常被称为 "counting hash"。有关解释,请参阅 class 方法 Hash::new 的文档。
正则表达式匹配字符串的第一个字母或一个或多个数字。例如,
"ASLK 50".scan(r)
#=> ["A", "50"]
我有两个数组。第一个将是一个包含名称和数量的字符串数组。第二个是字母数组。
a1 = ["ASLK 50", "BSKD 150", "ZZZZ 100", "BSDF 50"]
a2 = ["B", "Z"]
我想创建第三个数组来根据 a2 对 a1 中的内容进行排序,return 根据第一个数组中的信息对数字进行排序。由于 a2 有 "B" 和 "Z",我需要扫描第一个数组以查找以字母 B 和 Z 开头的所有条目并将数字相加。
我的最终目标是 return 第三个数组的总和,像这样:
a3 = ["B = 200", "Z = 100"]
因为"A"不在a2上,所以不算。
我能够从 a1 中提取信息:
arr = a1.map{|el| el[0] + " : " + el.gsub(/\D/, '')}
#=> ["A : 50", "B : 150", "Z : 100", "B : 50"]
我无法比较 a1 和 a2。我尝试过不同的方法,例如:
a1.find_all{|i| i[0] == a2[0]} #=> only returns the first element of a2. How can I iterate through a2?
或者,
i = 0
arr_result = []
while i < (arr.length + 1)
#(append to arr_result the number from a1 if a1[i][0] matches a2[i])
我认为两者都可以解决它,但我不能将这两个想法都归结为工作代码。我怎样才能实施这两种方法?有没有更有效的方法呢?
我会这样做:
a1 = ["ASLK 50", "BSKD 150", "ZZZZ 100", "BSDF 50"]
a2 = ["B", "Z"]
a3 = a1.each_with_object(Hash.new(0)) do |a,obj|
obj[a[0]] += a.split.last.to_i if a2.include?(a[0])
end.map {|a| a.join(" = ")}
#=>["B = 200", "Z = 100"]
第一步通过将第二个数组中包含的每个第一个字母的值相加,将它们全部添加到 Hash
中。
第二步提供所需的输出
如果您想要一个 Hash,只需取消对 map 的最后一次调用即可。
{"B" => 200, "Z" => 100}
运行你的要求,就是你要转这个:
a1 = ["ASLK 50", "BSKD 150", "ZZZZ 100", "BSDF 50"]
a2 = ["B", "Z"]
进入这个:a3 = ["B = 200", "Z = 100"]
a3 = a2.map do |char|
sum = a1.reduce(0) do |sum, item|
name, price = item.split(" ")
sum += price.to_i if name[0].eql?(char)
sum
end
"#{char} = #{sum}"
end
与其将 Array
定义为 ["B = 200", "Z = 100"]
,不如将其定义为 映射 - 即以下 Hash
对象:
{"B" => 200, "Z" => 100}
至于实现,有很多方法可以实现。这只是一种方法:
a1 = ["ASLK 50", "BSKD 150", "ZZZZ 100", "BSDF 50"]
a2 = ["B", "Z"]
result = a2.map do |letter|
[
letter,
a1.select {|str| str[0] == letter}
.inject(0) {|sum, str| sum += str[/\d+/].to_i}
]
end.to_h
puts result # => {"B"=>200, "Z"=>100}
解释:
- 在顶层,我使用
Array#to_h
将数组对:[["B", 200], ["Z", 100]]
转换为Hash
:{"B" => 200, "Z" => 100}
. a1.select {|str| str[0] == letter}
仅选择a1
中首字母为哈希键的元素。inject(0) {|sum, str| sum += str[/\d+/].to_i}
将所有数字相加,安全防护默认为零(而不是nil
意外抛出)。
这与@engineersmnky 的回答非常相似。
r = /
\A[A-Z] # match an upper case letter at the beginning of the string
| # or
\d+ # match one or more digits
/x # free-spacing regex definition mode
a1.each_with_object(Hash.new(0)) do |s,h|
start_letter, value = s.scan(r)
h[start_letter] += value.to_i if a2.include?(start_letter)
end.map { |k,v| "#{k} = #{v}" }
#=> ["B = 200", "Z = 100"]
Hash.new(0)
通常被称为 "counting hash"。有关解释,请参阅 class 方法 Hash::new 的文档。
正则表达式匹配字符串的第一个字母或一个或多个数字。例如,
"ASLK 50".scan(r)
#=> ["A", "50"]