【问题标题】:Finding similar elements of an array within a value of a hash in Ruby在 Ruby 中的哈希值中查找数组的相似元素
【发布时间】:2014-12-18 15:53:24
【问题描述】:

我有以下哈希:

hash = {"One" => [1,2,3,4], "Two" => [1,5,6,7], "Three" => [1,8,9,10]}

如果散列中每个数组的第一个元素以 1 开头,则放置一个字符串的条件语句。

if hash.values.all?{|array| array[0] == 1}
  puts "Hello World"
end

有没有办法改变输入的块来检查共享相同索引的值的所有组合是否也共享相同的值?

例子:

hash = {"One" => [4,1,3,11], "Two" => [14,1,6,7], "Three" => [12,1,9,10]}

if hash.values.all?{|array| array[0 or 1 or 2 or 3] == 1} (I know this is not valid Ruby code)
  puts "Hello World"
end

更新:

hash["One"]、hash["Two"] 和 hash["Three"] 目前都包含数组作为它们的值。在它们的数组中,元素 1 位于它们的每个 [1] 索引位置。我需要运行一个条件语句,如果一个元素(例如 1)位于多个数组的相同索引位置,每个数组都嵌套为哈希中的键值,它会放置一个字符串。请注意,元素必须在每个数组中并且在相同的索引位置才能通过。

hash = {"One" => [1,2,3,4], "Two" => [1,5,6,7], "Three" => [1,8,9,10]} # => True, 1 is in the [0] position for each array

hash = {"One" => [1,2,3,4], "Two" => [15,5,6,7], "Three" => [1,8,9,10]} # => Fail, 1 doesn't exist in the second array

hash = {"One" => [1,2,3,4], "Two" => [4,1,6,7], "Three" => [1,8,9,10]} # => Fail, 1 exists in the [0] position of the first and third array but [1] in the second array

hash = {"One" => [4,1,3,4], "Two" => [9,1,6,7], "Three" => [55,1,9,10]} # => True, 1 is in the [1] position for each array

【问题讨论】:

  • 您的意思是检查数组中的所有是否相同(不是索引)?例如,[4,4,4,4]?如果是这样,您必须使用嵌入式检查,例如 hash.values.all? { |array| array.all? {|x| x == 4 } }
  • 不,如果 array1 = [1,2,3] 和 array2 = [5,2,10],我需要检查 array1[1] 是否与 array2[1] 相同。 .但是在上面列出的情况下。
  • 您能否在您的问题陈述中引用一个完整的示例来说明这一点?现在还不清楚你在追求什么。您的示例对上下文使用了无效构造array[0..5],它本身就是一个数组,由array 中索引为0 到5 的元素组成,不可能等于1。如果你说出你想要的意思会更清楚。
  • 这正是我所需要的!非常感谢,请把它作为一个答案,这样我就可以给你最好的答案:)
  • 我还是不明白这个问题。您能否添加一些示例哈希并为每个哈希提供预期或期望的输出(只是truefalse?),以及“为什么”(如果合适)?如果不需要所有数组(值)的大小相同,请在您的示例中反映这一点。

标签: ruby arrays hash


【解决方案1】:

这是一种有趣的方法,可以确保您的所有数组至少有一个槽,槽中的值相同:

hash = {"One" => [4,1,3,11], "Two" => [14,1,6,7], "Three" => [12,1,9,10]}
if hash.values.transpose.map(&:uniq).any?{ |a| a.length==1 }
  # at least one of the 'slots' has all the same value
end

明确地说,此代码将匹配以下任一:

a = { one:[8,9,6], two:[1,9,2], three:[0,9,0] }
b = { one:[7,7,7], two:[6,6,7], three:[4,3,7] }

...因为在这两种情况下,所有数组中都有一个“槽”具有相同的值。 (a 中的第二个插槽都是9b 中的第三个插槽都是7。)

不匹配:

c = { one:[1,1,1], two:[2,2,2], three:[3,3,3] }

因为虽然每个数组都有相同的值,但每个对应槽中的值都是唯一的,即每个索引中的[1,2,3]


或者,如果这些值不仅必须相同,而且必须是该索引的值——即0 用于第一个索引,1 用于第二个,依此类推——然后你可以这样做:

if hash.values.transpose.map(&:uniq).select.with_index{ |a,i| a.length==1 && a.first==i }.first
  # at least one of the 'slots' is consistently filled with
  # a value that is the index of the slot
end

在这种情况下,abc 都不会匹配,但这会:

d = { one:[3,4,2,6], two:[8,1,2,2], three:[9,6,2,4] }

...因为第三个插槽(索引 #2)中始终有一个 2

类似(可以说更容易理解)的技术是:

if hash.values.transpose.to_enum(:any?).with_index{ |a,i| a.all?{ |n| n==i } }
  # yay!
end

说明

根据要求,让我们通过查看调用和中间值来了解这些答案:

解决方案 #1 - 任何插槽都具有相同的值(任何值)

hash = {"One" => [4,1,3,11], "Two" => [14,1,6,7], "Three" => [12,1,9,10]}

# Get an array of just the value parts of the hash
hash.values   
#=> [ [4,1,3,11], [14,1,6,7], [12,1,9,10] ]

# 'Rotate' the array of arrays, swapping 'rows' and 'columns'
hash.values.transpose  
#=> [ [4,14,12], [1,1,1], [3,6,9], [11,7,10] ] 

# Convert each of the new arrays to the set of distinct values in the array
# Equivalent to hash.values.transpose.map{ |a| a.uniq }
hash.values.transpose.map(&:uniq)
#=> [ [4,14,12], [1], [3,6,9], [11,7,10] ] 

# Test to see if any of these arrays has only one value in it
# i.e. If the array started out with all the same values
hash.values.transpose.map(&:uniq).any?{ |a| a.length==1 }
#=> true    (the second array-of-unique-values has only one element)

解决方案 2a:任何插槽都具有该索引处的所有值

我们已经覆盖了上面的hash.values.transpose.map(&:uniq),所以我们将从那里开始:

slot_values = hash.values.transpose.map(&:uniq)

# Create an Enumerator that selects items
# Normally select would take a block and return the matching values;
# When called without a block it gives you a 'delayed' form, the Enumerator
slot_values.select
#=> #<Enumerator: [[4,14,12],[1,1,1],[3,6,9],[11,7,10]]:any?>

# Add an index to the values during enumeration
slot_values.select.with_index
#=> #<Enumerator: #<Enumerator: [[4,14,12],[1,1,1],[3,6,9],[11,7,10]]:any?>:with_index>

# Invoke the enumerator, getting passed an array and the index
# Choose to select the array only if it has one element,
# and that element is the same value as the current index
slot_values.select.with_index{ |a,i| a.length==1 && a.first==i }
#=> [[1]]

# `.first` will return `nil` if the select returned no results,
# or the first array if there was at least one result.
# Because `nil` is considered "non-truthy", but an array is "truthy"
# We can use this as the condition for our if statement:
if slot_values.select.with_index{ |a,i| a.length==1 && a.first==i }.first
#=> …we enter the if statement because `.first` returns `[1]`…

解决方案 2b:任何插槽都具有该索引处的所有值

我们将从转置值开始:

slotvals = hash.values.transpose
#=> [ [4,14,12], [1,1,1], [3,6,9], [11,7,10] ] 

# Create an Enumerator (delayed evaluation) of the `any?` method
slotvals.to_enum(:any?)
#=> #<Enumerator: [[4,14,12],[1,1,1],[3,6,9],[11,7,10]]:any?>

# Add an index to the values that will be yielded to the enumerator
slotvals.to_enum(:any?).with_index
#=> #<Enumerator: #<Enumerator: [[4,14,12],[1,1,1],[3,6,9],[11,7,10]]:any?>:with_index>

# Invoke the enumerator, getting passed the array of slot values and the slot index
# For each array/index see if every value of the array equals the slot index
slotvals.to_enum(:any?).with_index{ |a,i| a.all?{ |n| n==i } }
#=> true  (bc the second array was all 1 values, which matched the array's index)

有关枚举器的更多详细信息,请阅读documentation on Enumerator

【讨论】:

  • 您可以通过将select 替换为any? 并删除.first 来简化最后一个。
  • @CarySwoveland 我原以为我也可以,但 .any? 没有块只会在 Ruby 2.0 中返回 true
  • 很高兴知道。至少我一直保持着每次不测试时都会出错的完美记录。
  • @CarySwoveland LOL :) FWIW 我发现.to_enum(:any?).with_index 的工作原理与我们都希望的一样。
  • 我不知道to_enum 可以接受争论。很高兴知道。另外,我喜欢你使用all?each_with_index.any? 代替 .to_enum(:any?).with_index? (我确实做了一项测试。)
猜你喜欢
  • 2014-12-18
  • 2015-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-31
  • 1970-01-01
  • 2023-04-04
  • 1970-01-01
相关资源
最近更新 更多