如何返回变量所在的数组中的元素

时间:2017-02-14 18:03:58

标签: ruby

我有一个独特的排序数组:[2,4,6,8,10]

我有一个名为i的变量。如果i5,我想返回5介于其中的数组中的元素。在这种情况下[4,6]。如果i8,则为[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]

虽然这有效,但我希望看看是否有更好的方法来编写这种逻辑。

4 个答案:

答案 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)

如果您在排序数组中查找元素,“更好的方式”可能涉及bsearchbsearch_index

对中的第二个元素是数组中第一个大于变量的元素,因此bsearch_index可以直接返回它。在返回找到的元素和前一个元素之前,您需要检查它不是nil0

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有点慢,但不是那么激进。