Ruby club 2个多维key通过日期等key元素合并为1

Ruby club two multi-dimensional key into 1 via date and other key elements

我刚开始研究 ruby Sinatra。

我在从现有数组创建数组时遇到问题。
我有一个按日期分组的数组,每个元素都包含当天的所有条目。

{
    "2015-05-15": [{
        "minutes": 25,
        "key1": [{
            "some key1": "14",
            "some key": "subject here"
        }],
        "key2": [{
            "some key": "0/0"
        }],
        "key3": [{
            "some key": "5/5"
        }],
        "key4": [{
            "some key": 0.48
        }],
        "key5": [{
            "some key": "0.6"
        }],
        "key6": "2015-05-15"
    }, {
        "minutes": 25,
        "key1": [{
            "some key1": "14",
            "some key": "subject here"
        }],
        "key2": [{
            "some key": "0/0"
        }],
        "key3": [{
            "some key": "5/5"
        }],
        "key4": [{
            "some key": 0.48
        }],
        "key5": [{
            "some key": "0.6"
        }],
        "key6": "2015-05-15"
    }],
    "2015-05-25": [{
        "minutes": 25,
        "key1": [{
            "some key1": "14",
            "some key": "subject here"
        }],
        "key2": [{
            "some key": "0/0"
        }],
        "key3": [{
            "some key": "5/5"
        }],
        "key4": [{
            "some key": 0.48
        }],
        "key5": [{
            "some key": "0.6"
        }],
        "key6": "2015-05-25"
    }],
    "2015-06-10": [{
        "minutes": 25,
        "key1": [{
            "some key1": "14",
            "some key": "subject here"
        }],
        "key2": [{
            "some key": "0/0"
        }],
        "key3": [{
            "some key": "5/5"
        }],
        "key4": [{
            "some key": 0.48
        }],
        "key5": [{
            "some key": "0.6"
        }],
        "key6": "2015-06-10"
    }, {
        "minutes": 25,
        "key1": [{
            "some key1": "14",
            "some key": "subject here"
        }],
        "key2": [{
            "some key": "0/0"
        }],
        "key3": [{
            "some key": "5/5"
        }],
        "key4": [{
            "some key": 0.48
        }],
        "key5": [{
            "some key": "0.6"
        }],
        "key6": "2015-06-10"
    }]
}

我想合并这个数组,以便所有子元素数组都通过每个日期的键排列。例如,下面的数组就是我要找的。这里 date 2015-05-15 中的所有子数组都组合成一个关键元素。

 {
    "2015-05-15": [{
        "minutes": 50,
        "key1": [{
            "some key1": "14",
            "some key": "subject here"
        },
        {
            "some key1": "14",
            "some key": "subject here"
        }],
        "key2": [{
            "some key": "0/0"
        },{
            "some key": "0/0"
        }],
        "key3": [{
            "some key": "5/5"
        },
        {
            "some key": "5/5"
        }],
        "key4": [{
            "some key": 0.48
        },
        [{
            "some key": 0.48
        }],
        "key5": [{
            "some key": "0.6"
        },
        {
            "some key": "0.6"
        }],
        "key6": "2015-05-15"
    }],
    "2015-05-25": [{
        "minutes": 50,
        "key1": [{
            "some key1": "14",
            "some key": "subject here"
        },
        {
            "some key1": "14",
            "some key": "subject here"
        }],
        "key2": [{
            "some key": "0/0"
        },{
            "some key": "0/0"
        }],
        "key3": [{
            "some key": "5/5"
        },
        {
            "some key": "5/5"
        }],
        "key4": [{
            "some key": 0.48
        },
        [{
            "some key": 0.48
        }],
        "key5": [{
            "some key": "0.6"
        },
        {
            "some key": "0.6"
        }],
        "key6": "2015-05-25"
    }],
    "2015-06-10": [{
        "minutes": 50,
        "key1": [{
            "some key1": "14",
            "some key": "subject here"
        },
        {
            "some key1": "14",
            "some key": "subject here"
        }],
        "key2": [{
            "some key": "0/0"
        },{
            "some key": "0/0"
        }],
        "key3": [{
            "some key": "5/5"
        },
        {
            "some key": "5/5"
        }],
        "key4": [{
            "some key": 0.48
        },
        [{
            "some key": 0.48
        }],
        "key5": [{
            "some key": "0.6"
        },
        {
            "some key": "0.6"
        }],
        "key6": "2015-06-10"
    }]
}

我尝试使用我创建的自定义方法来排列它们。

def self.iterateArray(array)
    result = Hash.new  #{ |h, k| h[k] = [] }
    count =  0
    result[count] = Array.new(7)

    array.each { |key, data|

      result[count]["date"] = key
      data.each { |k, d| 

        k.each { |key_, data1|
          if key_ == 'key1'
            result[count][key_] += data1
          end
          if key_ == 'key2'
            result[count][key_] << data1
          end
          if key_ == 'key3'
            result[count][key_] << data1
          end
          if key_ == 'key4'
            result[count][key_] << data1
          end
          if key_ == 'key5'
            result[count][key_] << data1
          end
          if key_ == 'key6'
            result[count][key_] << data1
          end
        }
      }
      count += 1
    }
    puts "result: #{result}"  
    result
  end

但每次我尝试 运行 这种方法时,我都会遇到奇怪的错误,例如 "no implicit conversion of String into Integer" at

result[count]["date"] = key

早些时候它在 result[count] 处创建了这样的错误,但后来我用

启动了它
result[count] = Hash.new { |h, k| h[k] = [] }

系统开始指向下一个元素。

谁能告诉我我做错了什么?或者告诉我如何处理此类问题或如何创建自定义数组。感谢任何帮助。

伙计们,很抱歉在这里添加了 2 个 "key2" 元素。它实际上是 key1 .. to key6 元素。我已经更新了示例数组。

你的问题有问题

  • 您在每个哈希(输入和输出)中有两次键 "key2"。因此,后者将覆盖第一个赋值的值。来自 engineersmnky 的示例:{key2: 1, key2: 2} #=> {key2: 2}

  • 您没有解释将输入转换为输出的完整逻辑。

  • 不知道是否要根据值的类型准备生成的散列,即 IntegerArray 等,还是它是特定于键的,即总是求和在不检查其类型的情况下增加键 "minutes" 的值。

解决方案

因此,虽然我不确定您预期输出背后的逻辑,但我能够将您的原始数组转换为它:

输入:

input = {
  "2015-05-15" => [{
    "minutes" => 25,
    "key1" => [{
        "some key1" => "14",
        "some key" => "subject here"
    }],
    "key2" => [{
        "some key" => "5/5"
    }],
    "key3" => [{
        "some key" => 0.48
    }],
    "key4" => [{
        "some key" => "0.6"
    }],
    "key5" => "2015-05-15"
}, {
    "minutes" => 25,
    "key1" => [{
        "some key1" => "14",
        "some key" => "subject here"
    }],
    "key2" => [{
        "some key" => "5/5"
    }],
    "key3" => [{
        "some key" => 0.48
    }],
    "key4" => [{
        "some key" => "0.6"
    }],
    "key5" => "2015-05-15"
}],
"2015-05-25" => [{
    "minutes" => 25,
    "key1" => [{
        "some key1" => "14",
        "some key" => "subject here"
    }],
    "key2" => [{
        "some key" => "5/5"
    }],
    "key3" => [{
        "some key" => 0.48
    }],
    "key4" => [{
        "some key" => "0.6"
    }],
    "key5" => "2015-05-25"
}],
"2015-06-10" => [{
    "minutes" => 25,
    "key1" => [{
        "some key1" => "14",
        "some key" => "subject here"
    }],
    "key2" => [{
        "some key" => "5/5"
    }],
    "key3" => [{
        "some key" => 0.48
    }],
    "key4" => [{
        "some key" => "0.6"
    }],
    "key5" => "2015-06-10"
}, {
    "minutes" => 25,
    "key1" => [{
        "some key1" => "14",
        "some key" => "subject here"
    }],
    "key2" => [{
        "some key" => "5/5"
    }],
    "key3" => [{
        "some key" => 0.48
    }],
    "key4" => [{
        "some key" => "0.6"
    }],
    "key5" => "2015-06-10"
  }]
}

程序:

output = {}

input.each do |date, ary|
  keys = ary.map(&:keys).flatten.uniq    # Returns all the keys in all the hash elements for given date

  hash = {}

  keys.each do |key|
    if ary.all? { |e| e[key].is_a?(Integer) }
      hash[key] = ary.inject(0) { |sum, h| sum + h[key].to_i }    # Assuming you want to sum up values for keys with integer values
    elsif ary.all? { |e| e[key].is_a?(Array) }
      hash[key] = ary.inject([]) { |sum, h| sum + h[key] }        # Assuming you need `+` operation on the array values too 
    elsif ary.all? { |e| e[key].is_a?(String) }
      hash[key] = ary[0][key]                                     # Assuming string values are same in all the elements of array
    else
      raise "Invalid type for value of key: #{key}"
    end
  end
  output[date] = [hash]     # I don't know why are we putting it in array as it will always be one element only. You could do: `output[date] = hash`
end

输出:

output
 => {"2015-05-15"=>[{
       "minutes"=>50,
       "key1"=>[{"some key1"=>"14", "some key"=>"subject here"},
                {"some key1"=>"14", "some key"=>"subject here"}],
       "key2"=>[{"some key"=>"5/5"}, {"some key"=>"5/5"}],
       "key3"=>[{"some key"=>0.48}, {"some key"=>0.48}],
       "key4"=>[{"some key"=>"0.6"}, {"some key"=>"0.6"}],
       "key5"=>"2015-05-15"}],
     "2015-05-25"=>[{
       "minutes"=>25,
       "key1"=>[{"some key1"=>"14", "some key"=>"subject here"}],
       "key2"=>[{"some key"=>"5/5"}],
       "key3"=>[{"some key"=>0.48}],
       "key4"=>[{"some key"=>"0.6"}],
       "key5"=>"2015-05-25"
     }],
     "2015-06-10"=>[{
       "minutes"=>50,
       "key1"=>[{"some key1"=>"14", "some key"=>"subject here"},
                {"some key1"=>"14", "some key"=>"subject here"}],
       "key2"=>[{"some key"=>"5/5"}, {"some key"=>"5/5"}],
       "key3"=>[{"some key"=>0.48}, {"some key"=>0.48}],
       "key4"=>[{"some key"=>"0.6"}, {"some key"=>"0.6"}],
       "key5"=>"2015-06-10"
     }]
   }

代码

def convert(h)
  h.each_with_object(Hash.new { |e,k| e[k]={} }) do |(date, v),f|
    v.each { |g| f[date].update(g) { |_,ov,nv|ov.is_a?(Array) ? ov+nv : ov } }
  end
end

例子

由于示例的大小和格式较大,因此很难只见树木不见森林。我做的第一件事是将示例缩减为基本结构。我将键(日期字符串)的数量从三个减少到两个,并减少了每个散列中的键数。我还使用缩进来阐明结构。通过这样做,我获得了以下内容。

h = {
  "2015-05-15"=>[
    { "minutes"=>25,
      "key1"=>[{ "some key1"=>"14", "some key"=>"subject here" }],
      "key2"=>[{ "some key"=>"5/5"}],
      "key5"=>"2015-05-15"
    },
    { "minutes"=>25,
      "key1"=>[{ "some key1"=>"14", "some key"=>"subject here" }],
      "key2"=>[{ "some key"=>"5/5" }],
      "key4"=>[{ "some key"=>"0.6" }],
      "key5"=>"2015-05-15"
    }
  ],
  "2015-06-10"=>[
    { "minutes"=>25,
      "key1"=>[{ "some key1"=>"14", "some key"=>"subject here" }],
      "key2"=>[{ "some key"=>"5/5" }],
      "key5"=>"2015-06-10"
    },
    { "minutes"=>25,
      "key1"=>[{ "some key1"=>"14", "some key"=>"subject here" }],
      "key2"=>[{ "some key"=>"5/5"}]
    }
  ]
}

convert(h)
  #=> { "2015-05-15"=>{
  #       "minutes"=>25, 
  #       "key1"=>[{"some key1"=>"14", "some key"=>"subject here"},
  #                {"some key1"=>"14", "some key"=>"subject here"}],
  #       "key2"=>[{"some key"=>"5/5"},
  #            {"some key"=>"5/5"}],
  #       "key5"=>"2015-05-15",
  #       "key4"=>[{"some key"=>"0.6"}]},
  #     "2015-06-10"=>{
  #       "minutes"=>25,
  #       "key1"=>[{"some key1"=>"14", "some key"=>"subject here"},
  #                {"some key1"=>"14", "some key"=>"subject here"}],
  #       "key2"=>[{"some key"=>"5/5"},
  #                {"some key"=>"5/5"}],
  #       "key5"=>"2015-06-10"
  #     }
  #   }

此结果与要求的略有不同。每个日期字符串键(例如 "2015-05-15")的值是一个包含单个元素(散列)的数组。让数组始终包含单个元素是没有意义的,因此我通过将日期字符串的值设为哈希值来简化。

说明

h 没有键 k 时,

Hash.new { |h,k| h[k]={} } 导致 h[k] 被设置为一个空散列。请参阅使用块的 Hash::new 形式。

我使用了 Hash#update (a.k.a. merge!) that employs a block to determine the values of keys that are present in both hashes being merged. See the doc for the values of the three block variables,k(common key),ov("*old value*") andnv` (新值" )

代码大致等同于以下内容。

f = {}
h.each do |date, v|
  v.each do |g|
    f[date] = {} unless f.key?(date)
    f[date].update(g) { |_,ov,nv| ov.is_a?(Array) ? ov+nv : ov }
  end
end
f

如果仍然不清楚,请尝试 运行 添加了一些 puts 语句的代码。

f = {}
h.each do |date, v|
  puts "date=#{date}"
  puts "v=#{v}"
  puts "f=#{f}"
  v.each do |g|
    puts "  f=#{g}"
      if f.key?(date)
        puts "  f has key date=#{date} so f[#{date}] is not set to {}"
      else
        puts "  f does not has key date=#{date} so f[#{date}] is to {}"
      end
    f[date] = {} unless f.key?(date)
    f[date].update(g) { |_,ov,nv| ov.is_a?(Array) ? ov+nv : ov }
    puts "  f[#{date}]= #{f[date]}"
  end
end
f

这将打印以下内容。

date=2015-05-15
v=[{"minutes"=>25, "key1"=>[{"some key1"=>"14", "some key"=>"subject here"}], 
                   "key2"=>[{"some key"=>"5/5"}], "key5"=>"2015-05-15"},
   {"minutes"=>25, "key1"=>[{"some key1"=>"14", "some key"=>"subject here"}],
                   "key2"=>[{"some key"=>"5/5"}], "key4"=>[{"some key"=>"0.6"}],
                   "key5"=>"2015-05-15"}]
f={}
f={"minutes"=>25, "key1"=>[{"some key1"=>"14", "some key"=>"subject here"}],
                  "key2"=>[{"some key"=>"5/5"}], "key5"=>"2015-05-15"}
f does not has key date=2015-05-15 so f[2015-05-15] is to {}
f[2015-05-15]= {"minutes"=>25,
                "key1"=>[{"some key1"=>"14", "some key"=>"subject here"}],
                "key2"=>[{"some key"=>"5/5"}], "key5"=>"2015-05-15"}
f={"minutes"=>25, "key1"=>[{"some key1"=>"14", "some key"=>"subject here"}],
                  "key2"=>[{"some key"=>"5/5"}],
                  "key4"=>[{"some key"=>"0.6"}],
                  "key5"=>"2015-05-15"}
f has key date=2015-05-15 so f[2015-05-15] is not set to {}
f[2015-05-15]= {"minutes"=>25,
                "key1"=>[{"some key1"=>"14", "some key"=>"subject here"},
                         {"some key1"=>"14", "some key"=>"subject here"}],
                "key2"=>[{"some key"=>"5/5"},
                         {"some key"=>"5/5"}],
                "key5"=>"2015-05-15",
                "key4"=>[{"some key"=>"0.6"}]}

date=2015-06-10
v=[{"minutes"=>25,
    "key1"=>[{"some key1"=>"14", "some key"=>"subject here"}],
    "key2"=>[{"some key"=>"5/5"}],
    "key5"=>"2015-06-10"},
       {"minutes"=>25,
        "key1"=>[{"some key1"=>"14", "some key"=>"subject here"}],
        "key2"=>[{"some key"=>"5/5"}]}]
f={"2015-05-15"=>{"minutes"=>25,
                  "key1"=>[{"some key1"=>"14", "some key"=>"subject here"}, 
                          {"some key1"=>"14", "some key"=>"subject here"}],
                  "key2"=>[{"some key"=>"5/5"},
                          {"some key"=>"5/5"}],
                  "key5"=>"2015-05-15", 
                  "key4"=>[{"some key"=>"0.6"}]}}
f={"minutes"=>25,
   "key1"=>[{"some key1"=>"14", "some key"=>"subject here"}],
   "key2"=>[{"some key"=>"5/5"}],
   "key5"=>"2015-06-10"}
f does not has key date=2015-06-10 so f[2015-06-10] is to {}

f[2015-06-10]= {"minutes"=>25,
                "key1"=>[{"some key1"=>"14", "some key"=>"subject here"}],
                "key2"=>[{"some key"=>"5/5"}], "key5"=>"2015-06-10"}
f={"minutes"=>25,
   "key1"=>[{"some key1"=>"14", "some key"=>"subject here"}],
   "key2"=>[{"some key"=>"5/5"}]}
f has key date=2015-06-10 so f[2015-06-10] is not set to {}
f[2015-06-10]= {"minutes"=>25,
                "key1"=>[{"some key1"=>"14", "some key"=>"subject here"},
                         {"some key1"=>"14", "some key"=>"subject here"}], 
                "key2"=>[{"some key"=>"5/5"}, {"some key"=>"5/5"}], 
                "key5"=>"2015-06-10"}

我已经使用我创建的这个方法得到了我的答案。在这里,我删除了重复出现的 "date" 字段。

 def self.iterateArray(array)
    response = []
    array.each { |key, data|
      result = Hash.new { |h, k| h[k] = [] }

      result["date"] = key
      result["minutes"] =0
      result["key1"] =[]
      result["key2"] =[]
      result["key3"] =[]
      result["key4"] =[]
      result["key5"] =[]
      data.each { |k, d| 

        k.each { |key1, data1|
          if key1 == "minutes"
            result[key1] += data1
          elsif key1 != "date"
            result[key1] << data1[0]
          end
        }
      }
      response << result
    }
    response
  end

但我仍然不喜欢我们需要在向数组中添加值之前声明所有变量的方式。有什么解决办法吗?