简体   繁体   English

Ruby将第一个数组中的元素与第二个数组进行匹配

[英]Ruby match elements from the first with second array

I have two arrays. 我有两个数组。 The first one will be an array of string with a name and the amount. 第一个将是一个包含名称和数量的字符串数组。 The second is an array of letters. 第二个是字母数组。

a1 = ["ASLK 50", "BSKD 150", "ZZZZ 100", "BSDF 50"]
a2 = ["B", "Z"]

I want to create a third array to sort the contents from a1 based off a2 and return the number based on the information from first array. 我想创建第三个数组,以基于a2对a1中的内容进行排序,并根据第一个数组中的信息返回数字。 Since a2 has "B" and "Z", I need to scan first array for all entry starting with letter B and Z and add up the numbers. 由于a2具有“ B”和“ Z”,因此我需要扫描第一个数组以查找所有以字母B和Z开头的条目,并将它们相加。

My ultimate goal is to return the sum on third array, something like this: 我的最终目标是在第三个数组上返回总和,如下所示:

a3 = ["B = 200", "Z = 100"]

Since "A" was not on a2, it is not counted. 由于“ A”不在a2上,因此不计算在内。

I was able to extract the information from a1: 我能够从a1中提取信息:

arr = a1.map{|el| el[0] + " : " + el.gsub(/\D/, '\1')}
#=> ["A : 50", "B : 150", "Z : 100", "B : 50"]

I am having trouble comparing a1with a2. 我在比较a1和a2时遇到问题。 I have tried different methods, such as: 我尝试了不同的方法,例如:

a1.find_all{|i| i[0] == a2[0]} #=> only returns the first element of a2. How can I iterate through a2?

alternatively, 或者,

i = 0
arr_result = []
while i < (arr.length + 1)
    #(append to arr_result the number from  a1 if a1[i][0] matches a2[i])

I think either would solve it, but I can't put neither idea down to working code. 我认为任何一个都可以解决它,但是我不能将两个想法都归结为工作代码。 How can I implement either method? 如何实现这两种方法? Is there a more efficient way to do it? 有更有效的方法吗?

Here is how I would do this: 这是我的处理方式:

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"]

First step adds them all into a Hash by summing the values by each first letter that is contained in the second Array. 第一步,通过将第二个数组中包含的每个第一个字母的值相加,将它们全部添加到Hash

Second step provides the desired output 第二步提供所需的输出

If you want a Hash instead just take off the last call to map and you'll have. 如果您想要哈希,只需取消最后一次调用map即可。

{"B" => 200, "Z" => 100}

Running with your requirements, that you want to turn this: 在满足您的要求的情况下,您要更改以下内容:

a1 = ["ASLK 50", "BSKD 150", "ZZZZ 100", "BSDF 50"]
a2 = ["B", "Z"]

into this: a3 = ["B = 200", "Z = 100"] 变成: 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

Rather than defining an Array like ["B = 200", "Z = 100"] , it would be more sensible to define this as a mapping - ie the following Hash object: 与其定义像["B = 200", "Z = 100"]这样的Array不如将其定义为映射 -即以下Hash对象,将更为明智:

{"B" => 200, "Z" => 100}

As for the implementation, there are many ways to do it. 至于实现,有很多方法可以做到。 Here is just one approach: 这只是一种方法:

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}

Explanation: 说明:

  • At the top level, I've used Array#to_h to convert the array of pairs: [["B", 200], ["Z", 100]] into a Hash : {"B" => 200, "Z" => 100} . 在顶层,我使用Array#to_h对的数组: [["B", 200], ["Z", 100]]转换为Hash{"B" => 200, "Z" => 100}
  • a1.select {|str| str[0] == letter} a1.select {|str| str[0] == letter} selects only the elements from a1 whose first letter is that of the hash key. a1.select {|str| str[0] == letter}仅从a1选择其第一个字母为哈希键的元素。
  • inject(0) {|sum, str| sum += str[/\\d+/].to_i} inject(0) {|sum, str| sum += str[/\\d+/].to_i} adds up all the numbers, with safe-guards to default to zero (rather than having nil thrown around unexpectedly). inject(0) {|sum, str| sum += str[/\\d+/].to_i}将所有数字加起来,并且安全防护默认为零(而不是意外地抛出nil )。

This is quite similar to @engineersmnky's answer. 这与@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) is often referred to as a "counting hash". Hash.new(0)通常被称为“计数哈希”。 See the doc for the class method Hash::new for an explanation. 有关说明,请参见文档中有关类方法Hash :: new的内容。

The regex matches the first letter of the string or one or more digits. 正则表达式匹配字符串的首字母或一个或多个数字。 For example, 例如,

"ASLK 50".scan(r)
  #=> ["A", "50"]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM