我有一个独特的排序数组:[2,4,6,8,10]
。
我有一个名为i
的变量。如果i
为5
,我想返回5
介于其中的数组中的元素。在这种情况下[4,6]
。如果i
为8
,则为[8,10]
。
我应该怎么做?
我在某种程度上尝试了partition
。如果i
碰巧是一个直接等于数组中某个值的数字。这似乎有效:
a=[2,4,6,8,10]
i = 6
a.partition { |v| v < i }.max[0..1] # returns [6,8]
但是,如果i
是一个不直接等于数组中任何值的数字。例如5
,它会变得有点棘手。
我让它适用于最后一个案例:
a=[2,4,6,8,10]
i = 5
partition = a.partition { |v| v < i }
[].tap { |a| a << partition[0].max; a << partition[1].min } # returns [6,8]
虽然这有效,但我希望看看是否有更好的方法来编写这种逻辑。
答案 0 :(得分:3)
您可以使用Enumerable#each_cons。
def mind_the_gap(arr, n)
arr.each_cons(2).find { |l,u| l <= n && n < u }
end
arr = [2,4,6,8,10]
mind_the_gap(arr, 5) #=> [4,6]
mind_the_gap(arr, 8) #=> [8,10]
mind_the_gap(arr, 1) #=> nil
mind_the_gap(arr, 10) #=> nil
如果您不希望最后两个示例返回nil
,则可以按如下方式更改方法。
def mind_the_gap(arr, n)
rv = arr.each_cons(2).find { |l,u| l <= n && n < u }
return rv unless rv.nil?
n < arr.first ? :low : :high
end
mind_the_gap(arr, 5) #=> [4,6]
mind_the_gap(arr, 8) #=> [8,10]
mind_the_gap(arr, 1) #=> :low
mind_the_gap(arr, 10) #=> :high
另一种方法是使用Enumerable#slice_when。
def mind_the_gap(arr, n)
a = arr.slice_when { |l,u| l <= n && n < u }.to_a
return [a.first.last, a.last.first] unless a.size == 1
n < arr.first ? :low : :high
end
mind_the_gap(arr, 5) #=> [4,6]
mind_the_gap(arr, 8) #=> [8,10]
mind_the_gap(arr, 1) #=> :low
mind_the_gap(arr, 10) #=> :high
答案 1 :(得分:2)
如果您在排序数组中查找元素,“更好的方式”可能涉及bsearch
或bsearch_index
。
对中的第二个元素是数组中第一个大于变量的元素,因此bsearch_index
可以直接返回它。在返回找到的元素和前一个元素之前,您需要检查它不是nil
或0
:
a = [2, 4, 6, 8, 10]
def find_surrounding_pair(array, element)
second_index = array.bsearch_index { |x| x > element }
array[second_index - 1, 2] if second_index && second_index > 0
end
puts find_surrounding_pair(a, 1).nil?
puts find_surrounding_pair(a, 2) == [2, 4]
puts find_surrounding_pair(a, 7) == [6, 8]
puts find_surrounding_pair(a, 8) == [8, 10]
puts find_surrounding_pair(a, 12).nil?
#=> true * 5
此方法的复杂性应为O(log n)
。
答案 2 :(得分:1)
这个怎么样
val = 5
a = [2,4,6,8,10] # assuming it's sorted
a.slice(a.rindex {|e| e <= val}, 2)
当查找值等于或大于数组的最后一个元素时,它不会考虑这种情况。我可能会为此添加一个零元素,如果这适合于问题。
答案 3 :(得分:1)
这看起来很适合检查范围内的包含:
a = [2,4,6,8,10]
b = 5
a.each_cons(2).select { |i, j| (i .. j) === b }
# => [[4, 6]]
目前尚不清楚“跌倒”的确切含义。在上面的代码中,8
将介于两组数字之间:
b = 8
a.each_cons(2).select { |i, j| (i .. j) === b }
# => [[6, 8], [8, 10]]
如果测试是i <= b <= j
。如果是i <= b < j
,则使用...
代替..
:
a.each_cons(2).select { |i, j| (i ... j) === b }
# => [[8, 10]]
我不是使用...
的忠实粉丝,但它简化了代码。
来自Range文档:
使用
..
构建的范围包括从开始到结束。使用...
创建的内容排除了结束值。
您可以将其更改为:
a.each_cons(2).select { |i, j| i <= b && b <= j }
或:
a.each_cons(2).select { |i, j| i <= b && b < j }
如果这些对你的想法更好。使用Range有点慢,但不是那么激进。