根据使用Ruby的条件,将值从一个数组拉入另一个数组的另一个数组的子数组中

时间:2015-12-31 21:05:53

标签: arrays ruby

我有一个数组数组:

exarray = [
  ["John Doe", "12/31/2015", "1504"],
  ["Jane Doe", "12/31/2015", "0904"],
  ["John Doe", "04/08/2015", "1300"],
  ["Jimmy Dean", "01/01/2014", "0406"],
  ["John Doe", "04/08/2015", "1402"],
  ["Jane Doe", "12/31/2015", "0908"],
  ["Jane Doe", "12/31/2015", "1045"]
]

我的最终目标是将它作为一个包含[user,date,firsttime,maxtime]的数组数组。例如:

array = [
  ["Jane Doe", "12/31/2015", "0904", "1045"],
  # Other users, dates, times
]

我已经在解决方案上工作了一段时间,此时我有上面的数组和另一个我创建的日期和arr用户数组,其中包含条目。例如:

new_array = ["12/31/2015", [["Jane Doe"], ["John Doe"]], ["OtherDates", [["UserA"],["UserB"]]]

我的想法是,我会从原始数组中收集时间作为数组并将其添加到每个用户,然后从那里去获取最小值/最大值。

new2_array =  ["12/31/2015", [["Jane Doe", ["0908","0904","1045"], ["John Doe",["1504"]], ["OtherDates", [["UserA",[Times]],["UserB",[Times]]]]

然而,我很难弄清楚如何将时间与用户和日期与我的新阵列相匹配。我已经尝试了几次使用map的尝试,每次尝试并没有成功收集,我相信我的逻辑错误。

new2_array = new_array.each do |n, d| 
  exarray.each do |sn, sd, st| 
    if sd ==bd && n.include?(sn) 
      st
    end
  end 
end

我尝试了各种地图,收集但没有成功。有没有更好的方法来完成我想要做的事情?我对编程非常陌生,只是教自己红宝石,但我已经阅读了一些有关如何完成我想做的新想法或灵感的阵列文档。

2 个答案:

答案 0 :(得分:1)

  

我的想法是,我将从原始数组中收集时间作为数组并将其添加到每个用户,然后从那里去获取最小/最大。

我认为这是一个非常复杂的数据结构,适合您要完成的任务。首先,尽量避免使用不同类型的对象数组(字符串,数组等的碰撞)。

为什么不喜欢

data = {
    "12/31/2015" => [
       {username: "Jane Doe", times: ["0908","0904","1045"], OtherDates: ['']}, 
       {username: "John", times: ["0908","0904","1045"], OtherDates: ['']}
    ]
} 

通过这种方式,您可以选择所需的日期,然后遍历所有员工对象,并获取目标数据。

例如,通过这种方式,您可以迭代以获得" max"每个用户的时间:

data["12/31/2015"].each do |i|
    puts "Username #{i['username']} max-time: #{i['times'].max}"
end

或为所有用户积累以获得最大值:

data["12/31/2015"].map do |i|
    i[:times]
end.flatten.max

答案 1 :(得分:1)

arr = [
  ["John Doe",   "12/31/2015", "1504"],
  ["Jane Doe",   "12/31/2015", "0904"],
  ["John Doe",   "04/08/2015", "1300"],
  ["Jimmy Dean", "01/01/2014", "0406"],
  ["John Doe",   "04/08/2015", "1402"],
  ["Jane Doe",   "12/31/2015", "0908"],
  ["Jane Doe",   "12/31/2015", "1045"]
]

arr.each_with_object({}) { |(name,date,val),h|
  h.update(name => { date: date, val: [val.to_i] }) { |_,h1,h2|
    { date: h1[:date], val: h1[:val] + h2[:val] } } }.
      map { |name, h| [name, h[:date], *h[:val].minmax.map { |n| "%04d" % n }] }
  #=> [["John Doe",   "12/31/2015", "1300", "1504"],
  #    ["Jane Doe",   "12/31/2015", "0904", "1045"],
  #    ["Jimmy Dean", "01/01/2014", "0406", "0406"]] 

我将解释它是如何工作的,并且还将尝试描述导致这个答案的思考过程。我意识到你是Ruby的新手,所以在第一次通过,甚至第四次通过时,它们可能都不是很有意义。

我们需要对arr的元素(数组)进行一些聚合或分组;也就是说,我们希望按名称对元素进行分组,这是arr的每个元素(数组)的第一个元素。当您想要聚合时,请考虑" hash",使用单个键作为聚合完成的(唯一)对象,此处为名称。有两种方法:从头开始构建哈希(从空哈希开始,{})或使用返回合适哈希的方法。这里适用的一种方法是Enumerable#group_by 1,2

arr.group_by { |a| a.first }
  #=> {"John Doe"  =>[["John Doe",   "12/31/2015", "1504"],
  #                   ["John Doe",   "04/08/2015", "1300"],
  #                   ["John Doe",   "04/08/2015", "1402"]],
  #    "Jane Doe"  =>[["Jane Doe",   "12/31/2015", "0904"],
  #                   ["Jane Doe",   "12/31/2015", "0908"],
  #                   ["Jane Doe",   "12/31/2015", "1045"]],
  #    "Jimmy Dean"=>[["Jimmy Dean", "01/01/2014", "0406"]]} 

我本可以使用group_by 3 ,但是选择了第一条路线,从头开始构建哈希。让我们从:

开始
h = {}

要构建哈希h,我们可以使用方法Hash#update(又名merge!)。例如,如果h = { :a=>1 },那么

h.update({ :b=>2 }) #=> { :a=>1, :b=>2 }

Ruby允许我们在没有括号的情况下编写它:

h.update(:b=>2) #=> { :a=>1, :b=>2 }

并在键是符号时使用简短形式:

h.update(b: 2) #=> { a: 1, b: 2 }

所以我从这里开始这样做。我们也有:

{ a: 1 }.update(a: 2) #=> { a: 2 }

我们想要的是:

{ a: [1] }.update(a: [2]) #=> { a: [1,2] }

我们可以使用update(参见文档)的形式获取它,该形式使用哈希来确定合并的两个哈希中存在的键的值:

arr.each { |a|
  h.update(a[0]=>{ date: a[1], val: [a[2].to_i] }) { |k,h1,h2|
    { date: h1[:date], val: h1[:val] + h2[:val] } } }

在仔细研究之前,让消除块变量a到其三个元素namedate和{{1 }}。我们有:

val

arr.each { |name,date,val| h.update(name=>{ date: date, val: [val.to_i] }) { |k,h1,h2| { date: h1[:date], val: h1[:val] + h2[:val] } } } 返回其接收方each,而不是arr的更新值,即:

h

我们可以按如下步骤进行计算:

  h #=> {"John Doe"  =>{:date=>"12/31/2015", :val=>[1504, 1300, 1402]},
    #    "Jane Doe"  =>{:date=>"12/31/2015", :val=>[904, 908, 1045]},
    #    "Jimmy Dean"=>{:date=>"01/01/2014", :val=>[406]}}

枚举器的第一个值enum = arr.each #=> #<Enumerator: [["John Doe", "12/31/2015", "1504"], # ["Jane Doe", "12/31/2015", "0904"], # ["John Doe", "04/08/2015", "1300"], # ["Jimmy Dean", "01/01/2014", "0406"], # ["John Doe", "04/08/2015", "1402"], # ["Jane Doe", "12/31/2015", "0908"], # ["Jane Doe", "12/31/2015", "1045"]]:each> enum)将传递给块,并使用并行分配(或多个赋值)分配块值)。我们可以使用Enumerator#next来模拟它:

["John Doe", "12/31/2015", "1504"]

并执行块计算:

name, date, val = enum.next
  #=> ["John Doe", "12/31/2015", "1504"] 
name
  #=> "John Doe"
date
  #=> "12/31/2015" 
val
  #=> "1504" 

返回值是h.update(name=>{ date: date, val: [val.to_i] }) #=> {}.update("John Doe"=>{ :date=>"12/31/2015", :val=>["1504"] }) #=> {"John Doe"=>{:date=>"12/31/2015", :val=>[1504]}} 的更新值。

由于我们将h合并到{ "John Doe"=>{ :date=>"12/31/2015", :val=>"1504" } },因此两个哈希没有共享密钥。因此,不使用用于确定值的块(我未在上面包括)。

现在{}enum)的第二个元素被传递给块并执行块计算:

["Jane Doe", "12/31/2015", "0904"]

同样,未使用用于确定值的块,因为两个哈希值(name, date, val = enum.next #=> ["Jane Doe", "12/31/2015", "0904"] name #=> "Jane Doe" date #=> "12/31/2015" val #=> "0904" h.update(name=>{ date: date, val: [val.to_i] }) #=> {"John Doe"=>{:date=>"12/31/2015", :val=>[1504]}}. # update("Jane Doe"=>{ :date=>"12/31/2015", :val=>["0904"] }) #=> {"John Doe"=>{:date=>"12/31/2015", :val=>[1504]}, # "Jane Doe"=>{:date=>"12/31/2015", :val=>[904]}} {"John Doe"=>{:date=>"12/31/2015", :val=>["1504"]}})没有共用键。

第三个值传递给块:

{ "Jane Doe"=>{ :date=>"12/31/2015", :val=>["0904"] } }

这次合并的两个哈希都有关键&#34; John Doe&#34;,因此该块用于确定&#34; John Doe&#34;的值。我们有 4

name, date, val = enum.next
  #=> ["John Doe", "04/08/2015", "1300"] 
h.update(name=>{ date: date,  val: [val.to_i] }) { |k,h1,h2|
  { date: h1[:date], val: h1[:val] + h2[:val] } }
  #=> h.update("John Doe"=>{ date: "04/08/2015",  val: [1300] }) { |k,h1,h2|
  { date: h1[:date], val: h1[:val] + h2[:val] } }
  #=> {"John Doe"=>{:date=>"12/31/2015", :val=>[1504, 1300]},
  #    "Jane Doe"=>{:date=>"12/31/2015", :val=>[904]}}

k #=> "John Doe" h1 #=> { date: "12/31/2015", val: [1504] } # "old" value h2 #=> { date: "04/08/2015", val: [1300] } # "new" value { date: h1[:date], val: h1[:val] + h2[:val] } #=> { date: "12/31/2015", val: [1504] + [1300] } #=> { date: "12/31/2015", val: [1504, 1300] } 的其余元素的计算类似。如上所示,结果是哈希:

enum

仍然可以将哈希值转换为所需的数组。这实际上很容易。它涉及计算内部哈希中每个键 h #=> {"John Doe" =>{:date=>"12/31/2015", :val=>[1504, 1300, 1402]}, # "Jane Doe" =>{:date=>"12/31/2015", :val=>[904, 908, 1045]}, # "Jimmy Dean"=>{:date=>"01/01/2014", :val=>[406]}} 的最小值和最大值,以及更改格式。如果min和max 5 需要整数值,我们可以这样做:

:val

由于最小值和最大值需要四个字符的字符串(带前导零),因此需要采取另一个步骤:

h.map { |k,v| [k, v[:date], v[:val].minmax] }
  #=> [["John Doe", "12/31/2015", [1300, 1504]],
  #    ["Jane Doe", "12/31/2015", [904, 1045]],
  #    ["Jimmy Dean", "01/01/2014", [406, 406]]]

由于这最后一步不是问题的核心,我将省略转换的解释。

最后:

  • 而不是定义 h.map { |k,v| [k, v[:date], v[:val].minmax.map { |n| "%04d" % n }] } ,首先,我使用方法Enumerable#each_with_object,&#34;对象&#34;是由块变量h = {}表示的哈希,它是方法返回的值。哈希的初始值由参数h
  • 给出
  • 由于块中的块变量{}用于确定合并的两个哈希中的键的值,因此不会在块计算中使用,我已将其更改为局部变量{{1这是惯例。
  • 我将哈希的构造链接到其映射到所需数组。

1当接收器k是一个数组时,您想要查找可能在类Array或模块{{}中使用的方法。 3}}。 _ arr d(&#34;混合)由多个类组成,Enumerable为一个类。同样,如果接收者是哈希,您可以查看班级Enumerableinclude

2很久以前的一天,来自旭日之地的一位非常聪明的人注意到他用于阵列的许多方法与他用于哈希,范围和其他收藏的方法非常相似。他看到他可以编写它们,唯一的区别就是方法Array是如何实现的,所以他把它们都放在一个他称之为“#34;可算の&#34; (&#34; Enumerable&#34;),然后在不同类型的集合的所有类中(EnumerableeachArrayHash等)他添加了Range和方法Set。在这之后,他想,&#34;生活は快适です&#34; (&#34;生活很好&#34;)。

3一旦您了解我采取的方法,看看您是否可以使用include Enumerable回答问题。

4由于each的{​​{1}}的{​​{1}}的所有元素具有相同的group_by,因此问题变得更容易了,所以我可以使用:dateenum

5计算数组的最小值和最大值是一项相当常见的任务,因此您应该期望Ruby提供一种方法来执行此操作。仔细阅读Hash的文档以获取此类方法。没有,所以试试Array。宾果:Enumerable