这是一种有趣的方法,可以确保您的所有数组至少有一个槽,槽中的值相同:
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 中的第二个插槽都是9,b 中的第三个插槽都是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
在这种情况下,a、b 或 c 都不会匹配,但这会:
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。