迭代哈希值,并获取值,直到达到某个总和限制

时间:2022-01-20 21:26:24

I have two arrays of hashes that look something like this:

我有两个哈希数组,看起来像这样:

h1=[{id:1, item:1, value:10},
    {id:1, item:2, value:3}]

h2=[{id:1, item_a:1, value:5},{id:2, item_a:1, value:7},{id:3, item_a:1, value:10},
    {id:4, item_b:2, value:1},{id:5, item_b:2, value:2},{id:6, item_b:2, value:5},
    {id:7, item_b:2, value:1}]

I need to iterate through h2 and:

我需要遍历h2和:

  1. for each h1 hash, find an h2 hash whose item corresponds to item_a, e.g., item value is 1 and item_a values is 1,
  2. 对于每个h1哈希,找到其项目对应于item_a的h2哈希,例如,项值为1且item_a值为1,

  3. take value from hash h2 until sum of the items taken are equal to or bigger than the one in h1 hash value,
  4. 从哈希值h2获取值,直到所采用的项的总和等于或大于h1哈希值中的项,

  5. create an array of arrays of ids from h1 and h2 whose first value is id from h1 and second value is id from h2.
  6. 从h1和h2创建一个id数组数组,其第一个值是来自h1的id,第二个值是来自h2的id。

In my example above, the result would be [[1,1],[1,2],[1,4],[1,5]].

在我上面的例子中,结果将是[[1,1],[1,2],[1,4],[1,5]]。

I've found this answer here, however it just sums up the values up to some defined limit. I don't know how to take the values until some total sum is reached part. I hope someone can give me direction where I should be looking at.

我在这里找到了这个答案,但它只是将值总结到某个定义的限制。我不知道如何获取值,直到达到总和的一部分。我希望有人可以指导我应该关注的方向。

Update

Result [[1,1],[1,2],[1,4],[1,5]] is because:

结果[[1,1],[1,2],[1,4],[1,5]]是因为:

  1. from h1 we take first hash where item:1 and value:10, so we need to collect ids from h2 where item_a:1 since I need 1 and 1 values (in real life - ids) and our total value should be at least 10
  2. 从H1我们把其中项目第一哈希:1和值:10,所以我们需要从那里item_a H2收集IDS:1,因为我需要1倍1的值(在现实生活中 - IDS)和我们的总价值至少应为10

  3. we iterate through h2 and start by first hash - its value:5 which is smaller than 10 so we take it and we go to next hash - its value:7 and if we sum it to previously taken 5, we get 12 - it is bigger than 10 so we take it and stop iteration here as we are not allowed to take any more item. Our result for first iteration is: id from h1 hash is 1 and ids from h2 hash are 1 and 2. So our array so far is [[1,1],[1,2]]
  4. 我们迭代h2并从第一个哈希开始 - 它的值:5小于10所以我们接受它并且我们转到下一个哈希 - 它的值:7如果我们将它加到之前的5,我们得到12 - 它是大于10所以我们采取它并停止迭代,因为我们不允许再采取任何项目。我们的第一次迭代的结果是:来自h1哈希的id是1,来自h2哈希的id是1和2.所以到目前为止我们的数组是[[1,1],[1,2]]

  5. we do same thing for second hash from h1, where our matched ids from h2 are 4 and 5.
  6. 我们对来自h1的第二个哈希做同样的事情,其中​​我们匹配的来自h2的id是4和5。

Update 2

I'm clearing out my example as there can be simply item in both hashes. I'd like to expand a bit my example.

我正在清理我的例子,因为两个哈希中都可以有简单的项目。我想扩展我的例子。

How would code change if extra criteria from-to date range and date criteria would be added in h1 and h2?

如果在h1和h2中添加额外的标准从最新范围和日期标准,代码将如何变化?

h1=[{id:1, item:1, from: DateTime.new(2017,9,4,6,0,0,'+0300'), to: DateTime.new(2017,9,4,17,59,59,'+0300'), value:10},
   {id:1, item:2, from: DateTime.new(2017,9,4,18,0,0,'+0300'), to: DateTime.new(2017,9,4,23,59,59,'+0300'), value:10}]

h2=[{id:1, item:1, date: DateTime.new(2017,9,4,6,10,0,'+0300'), value:5},
    {id:2, item:1, date: DateTime.new(2017,9,4,7,20,0,'+0300'), value:7},
    {id:3, item:1, date: DateTime.new(2017,9,4,8,05,0,'+0300'), value:10},
    {id:4, item:2, date: DateTime.new(2017,9,4,18,19,10,'+0300'), value:1},
    {id:5, item:2, date: DateTime.new(2017,9,4,19,20,0,'+0300'), value:2},
    {id:6, item:2, date: DateTime.new(2017,9,4,22,22,0,'+0300'), value:5},
    {id:7, item:2, date: DateTime.new(2017,9,4,23,0,0,'+0300'), value:1}]

I'd like to take ids from only those hashes where date from h2 is in range of h1 dates in from - to. I assume I should put this h2[:date].between?(h1[:from], h1[:to]) somewhere.

我想从只有那些哈希的日期中获取ids,其中h2的日期在h1的范围内,从 - 到。我想我应该把这个h2 [:date]。between?(h1 [:from],h1 [:to])放在某个地方。

2 个解决方案

#1


2  

First off a couple assumptions I'm making, you don't mention item_b at all in your description, and only state that the link between h1 and h2 is the key item_a, so I'm assuming that item_a and item_b are equivalent...ie, you won't have something like item_b: 1 that you wouldn't want to consider for the first hash in h1 just because it's item_b and not item_a. If that's not correct, you'll need to adjust the code below accordingly (shouldn't be too difficult).

首先是我正在做的几个假设,你在描述中根本没有提到item_b,并且只说明h1和h2之间的链接是关键item_a,所以我假设item_a和item_b是等价的。 .ie,你不会有类似item_b:1的东西,你不想考虑h1中的第一个哈希只是因为它是item_b而不是item_a。如果这不正确,您需要相应地调整下面的代码(不应该太难)。


To start with, it will be easier if you group the items in h2 by their item_a/item_b keys so you can just do a simple look up for them:

首先,如果您通过item_a / item_b键对h2中的项目进行分组会更容易,这样您就可以进行简单的查找:

h2 = h2.group_by { |item| item[:item_a] || item[:item_b] }
# => {1=>[ {:id=>1, :item_a=>1, :value=>5},
           {:id=>2, :item_a=>1, :value=>7},
           {:id=>3, :item_a=>1, :value=>10} ],
      2=>[ {:id=>4, :item_b=>2, :value=>1},
           {:id=>5, :item_b=>2, :value=>2},
           {:id=>6, :item_b=>2, :value=>5},
           {:id=>7, :item_b=>2, :value=>1} ]
     }

Now, h2[1] has all the items that map to the first row of h1 (based on the item key). Next, you want to map the values from h1 into a list of array with the id from h1 and the id from h2 and the ids from h2 come from a take_while loop, keeping track of a sum:

现在,h2 [1]具有映射到h1的第一行的所有项目(基于项目键)。接下来,您希望将h1中的值映射到一个数组列表,其中id为h1,id来自h2,而来自h2的id来自take_while循环,跟踪总和:

results = h1.map do |base|
  sum = 0

  h2[base[:item]].take_while do |item|
    sum += item[:value] if sum < base[:value]
  end.map do |item|
    [base[:id], item[:id]]
  end
end

# => [[[1, 1], [1, 2]], [[1, 4], [1, 5]]]

Lastly, you'll need to flatten by 1 level to get the result you want:

最后,你需要压平1级才能得到你想要的结果:

puts results.flatten(1).inspect
# => [[1, 1], [1, 2], [1, 4], [1, 5]]

#2


1  

I too have questions about the keys :item_a and :item_b (and presumably :item_c, etc.) in h2. One could assume :item_a pertains to h1[0], :item_b to h1[1], and so on, but it's easier to just give them all the same name, which I've chosen to be :item. That is, having item_a and item_b does not make it a more difficult problem, it just introduces a nuisance from a coding standpoint.

我也对h2中的键:item_a和:item_b(以及大概:item_c等)有疑问。可以假设:item_a属于h1 [0],:item_b到h1 [1],依此类推,但更容易给它们所有相同的名称,我选择它:item。也就是说,如果item_a和item_b没有使它成为一个更难的问题,它只是从编码的角度引入了一个麻烦。

h1 = [{ id:1, item:1, value:10 }, { id:1, item:2, value:3 }]
h2 = [{ id:1, item:1, value:5 },  { id:2, item:1, value:7 }, { id:3, item:1, value:10 },
      { id:4, item:2, value:1 },  { id:5, item:2, value:2 }, { id:6, item:2, value:5},
      { id:7, item:2, value:1 }]

I have assumed that, for each hash g in h1, if g[:value] is greater than the sum of f[:value] over all hashes f in h2 for which f[:item] = g[:item], [g[:id], f[:id], for all such hashes f, would be included in the array that is returned. (Whew!) If, instead, g should simply be disregarded is the sum of values from h2 insufficient, that requires only a small change to the code below.

我假设,对于h1中的每个散列g,如果g [:value]大于f [:value]与h2中所有散列f之和f [:item] = g [:item],[对于所有这样的散列f,g [:id],f [:id]将包含在返回的数组中。 (哇!)如果相反,g应该被忽略,那么来自h2的值的总和就不足了,这只需要对下面的代码进行一些小改动。

The first step is to construct another hash from h2.

第一步是从h2构造另一个哈希。

h = h2.each_with_object({}) do |g, h|
  h.update(g[:item]=>[[g[:id], g[:value]]]) do |_, o, n|
    o << [g[:id], o.last.last + g[:value]]
  end
end
  #=> {1=>[[1, 5], [2, 12], [3, 22]], 2=>[[4, 1], [5, 3], [6, 8], [7, 9]]}

As you see, h has keys equal to the values of :item and values that are arrays of pairs of :id values and "cumulative" values of :value.

如您所见,h的键值等于:item和值,它们是成对的数组:id值和value的“累积”值。

h1.each_with_object([]) do |g, a|
  id1 = g[:id]
  value1 = g[:value]
  arr = h[g[:item]]
  i = arr.index { |_id2, cum| value1 <= cum } || (arr.size - 1)
  arr[0..i].map(&:first).each { |id2| a << [id1, id2] }
end
  #=> [[1, 1], [1, 2], [1, 4], [1, 5]]

The steps following the calculation of h are as follows.

计算h后的步骤如下。

a = []
g = { id:1, item:1, value:10 }
id1 = g[:id]
  #=> 1
value1 = g[:value]
  #=> 10
arr = h[g[:item]]
  #=> h[1]
  #=> [[1, 5], [2, 12], [3, 22]]
i = arr.index { |_id2, cum| value1 <= cum } || (arr.size - 1)
  #=> 1

If value1 had been, say 100, we would obtain arr.index { |_id2, cum| value1 <= cum } #=> nil. In this case i is set equal to the "or'ed" term arr.size - 1.

如果value1已经是100,我们将获得arr.index {| _id2,cum | value1 <= cum}#=> nil。在这种情况下,我被设置为等于“or'ed”术语arr.size - 1。

b = arr[0..i]
  #=> [[1, 5], [2, 12]]
c = b.map(&:first)
  #=> [1, 2]
c.each { |id2| a << [id1, id2] }
a #=> [[1, 1], [1, 2]]

The remaining calculations are similar.

剩下的计算是类似的。

#1


2  

First off a couple assumptions I'm making, you don't mention item_b at all in your description, and only state that the link between h1 and h2 is the key item_a, so I'm assuming that item_a and item_b are equivalent...ie, you won't have something like item_b: 1 that you wouldn't want to consider for the first hash in h1 just because it's item_b and not item_a. If that's not correct, you'll need to adjust the code below accordingly (shouldn't be too difficult).

首先是我正在做的几个假设,你在描述中根本没有提到item_b,并且只说明h1和h2之间的链接是关键item_a,所以我假设item_a和item_b是等价的。 .ie,你不会有类似item_b:1的东西,你不想考虑h1中的第一个哈希只是因为它是item_b而不是item_a。如果这不正确,您需要相应地调整下面的代码(不应该太难)。


To start with, it will be easier if you group the items in h2 by their item_a/item_b keys so you can just do a simple look up for them:

首先,如果您通过item_a / item_b键对h2中的项目进行分组会更容易,这样您就可以进行简单的查找:

h2 = h2.group_by { |item| item[:item_a] || item[:item_b] }
# => {1=>[ {:id=>1, :item_a=>1, :value=>5},
           {:id=>2, :item_a=>1, :value=>7},
           {:id=>3, :item_a=>1, :value=>10} ],
      2=>[ {:id=>4, :item_b=>2, :value=>1},
           {:id=>5, :item_b=>2, :value=>2},
           {:id=>6, :item_b=>2, :value=>5},
           {:id=>7, :item_b=>2, :value=>1} ]
     }

Now, h2[1] has all the items that map to the first row of h1 (based on the item key). Next, you want to map the values from h1 into a list of array with the id from h1 and the id from h2 and the ids from h2 come from a take_while loop, keeping track of a sum:

现在,h2 [1]具有映射到h1的第一行的所有项目(基于项目键)。接下来,您希望将h1中的值映射到一个数组列表,其中id为h1,id来自h2,而来自h2的id来自take_while循环,跟踪总和:

results = h1.map do |base|
  sum = 0

  h2[base[:item]].take_while do |item|
    sum += item[:value] if sum < base[:value]
  end.map do |item|
    [base[:id], item[:id]]
  end
end

# => [[[1, 1], [1, 2]], [[1, 4], [1, 5]]]

Lastly, you'll need to flatten by 1 level to get the result you want:

最后,你需要压平1级才能得到你想要的结果:

puts results.flatten(1).inspect
# => [[1, 1], [1, 2], [1, 4], [1, 5]]

#2


1  

I too have questions about the keys :item_a and :item_b (and presumably :item_c, etc.) in h2. One could assume :item_a pertains to h1[0], :item_b to h1[1], and so on, but it's easier to just give them all the same name, which I've chosen to be :item. That is, having item_a and item_b does not make it a more difficult problem, it just introduces a nuisance from a coding standpoint.

我也对h2中的键:item_a和:item_b(以及大概:item_c等)有疑问。可以假设:item_a属于h1 [0],:item_b到h1 [1],依此类推,但更容易给它们所有相同的名称,我选择它:item。也就是说,如果item_a和item_b没有使它成为一个更难的问题,它只是从编码的角度引入了一个麻烦。

h1 = [{ id:1, item:1, value:10 }, { id:1, item:2, value:3 }]
h2 = [{ id:1, item:1, value:5 },  { id:2, item:1, value:7 }, { id:3, item:1, value:10 },
      { id:4, item:2, value:1 },  { id:5, item:2, value:2 }, { id:6, item:2, value:5},
      { id:7, item:2, value:1 }]

I have assumed that, for each hash g in h1, if g[:value] is greater than the sum of f[:value] over all hashes f in h2 for which f[:item] = g[:item], [g[:id], f[:id], for all such hashes f, would be included in the array that is returned. (Whew!) If, instead, g should simply be disregarded is the sum of values from h2 insufficient, that requires only a small change to the code below.

我假设,对于h1中的每个散列g,如果g [:value]大于f [:value]与h2中所有散列f之和f [:item] = g [:item],[对于所有这样的散列f,g [:id],f [:id]将包含在返回的数组中。 (哇!)如果相反,g应该被忽略,那么来自h2的值的总和就不足了,这只需要对下面的代码进行一些小改动。

The first step is to construct another hash from h2.

第一步是从h2构造另一个哈希。

h = h2.each_with_object({}) do |g, h|
  h.update(g[:item]=>[[g[:id], g[:value]]]) do |_, o, n|
    o << [g[:id], o.last.last + g[:value]]
  end
end
  #=> {1=>[[1, 5], [2, 12], [3, 22]], 2=>[[4, 1], [5, 3], [6, 8], [7, 9]]}

As you see, h has keys equal to the values of :item and values that are arrays of pairs of :id values and "cumulative" values of :value.

如您所见,h的键值等于:item和值,它们是成对的数组:id值和value的“累积”值。

h1.each_with_object([]) do |g, a|
  id1 = g[:id]
  value1 = g[:value]
  arr = h[g[:item]]
  i = arr.index { |_id2, cum| value1 <= cum } || (arr.size - 1)
  arr[0..i].map(&:first).each { |id2| a << [id1, id2] }
end
  #=> [[1, 1], [1, 2], [1, 4], [1, 5]]

The steps following the calculation of h are as follows.

计算h后的步骤如下。

a = []
g = { id:1, item:1, value:10 }
id1 = g[:id]
  #=> 1
value1 = g[:value]
  #=> 10
arr = h[g[:item]]
  #=> h[1]
  #=> [[1, 5], [2, 12], [3, 22]]
i = arr.index { |_id2, cum| value1 <= cum } || (arr.size - 1)
  #=> 1

If value1 had been, say 100, we would obtain arr.index { |_id2, cum| value1 <= cum } #=> nil. In this case i is set equal to the "or'ed" term arr.size - 1.

如果value1已经是100,我们将获得arr.index {| _id2,cum | value1 <= cum}#=> nil。在这种情况下,我被设置为等于“or'ed”术语arr.size - 1。

b = arr[0..i]
  #=> [[1, 5], [2, 12]]
c = b.map(&:first)
  #=> [1, 2]
c.each { |id2| a << [id1, id2] }
a #=> [[1, 1], [1, 2]]

The remaining calculations are similar.

剩下的计算是类似的。