将排序数组分区为连续数组的最佳方法是什么?

时间:2014-03-28 23:57:14

标签: ruby

是否有一种简单的方法或方法将数组分区为Ruby中连续数字的数组?

[1,2,3,5,6,8,10] => [[1,2,3],[5,6],[8],[10]]

我可以为此做一些例程,但想知道是否有一个快速的方法。

萨姆

5 个答案:

答案 0 :(得分:4)

我喜欢inject

numbers = [1, 2, 3, 5, 6, 8, 10]
contiguous_arrays = []
contiguous_arrays << numbers[1..-1].inject([numbers.first]) do |contiguous, n|
  if n == contiguous.last.succ
    contiguous << n
  else
    contiguous_arrays << contiguous
    [n]
  end
end

#=> [[1, 2, 3], [5, 6], [8], [10]] 

答案 1 :(得分:2)

一种方法,包括:

arr = [1,2,3,5,6,8,10] 

<强>#1

# If subarray is empty or the current value n is not the last value + 1,
# add the subarray [n] to the collection; else append the current value
# to the last subarray that was added to the collection. 

arr.each_with_object([]) { |n,a|
  (a.empty? || n != a.last.last+1) ? a << [n] : a[-1] << n }
  #=> [[1, 2, 3], [5, 6], [8], [10]]

<强>#2

# Change the value of 'group' to the current value n if it is the first
# element in arr or it is not equal to the previous element in arr + 1,
# then 'chunk' on 'group' and extract the result from the resulting chunked 
# array.

arr.map.with_index do |n,i|
  group = n if i == 0 || n != arr[i-1] + 1
  [n, group]
end.chunk(&:last)
   .map { |_,c| c.map(&:first) } 
  #=> [[1, 2, 3], [5, 6], [8], [10]] 

<强>#3

# If n is the last element of arr, append any number other than n+1 to
# a copy of arr and convert to an enumerator.  Step though the enumerator
# arr.size times, adding the current value to a subarray b, and using
# 'peek' to see if the next value of 'arr' equals the current value plus 1.
# If it does, add the subarray b to the collecton a and set b => [].

enum = (arr+[arr.last]).to_enum
a, b = [], []
arr.size.times do
  curr = enum.next
  b << curr
  (a << b; b = []) unless curr + 1 == enum.peek 
  end
end
a
  #=> [[1, 2, 3], [5, 6], [8], [10]] 

<强>#4

# Add elements n of arr sequentially to an array a, each time first inserting
# an arbitrary separator string SEP when n does not equal the previous value
# of arr + 1, map each element of a to a string, join(' '), split on SEP and
# convert each resulting array of strings to an array of integers.

SEP = '+'
match_val = arr.first
arr.each_with_object([]) do |n,a|
  (a << SEP) unless n == match_val 
  a << n
  match_val = n + 1
end.map(&:to_s)
   .join(' ')
   .split(SEP)
   .map { |s| s.split(' ').map(&:to_i) }
  #=> [[1, 2, 3], [5, 6], [8], [10]] 

arr包含负整数时,所有上述方法都有效。

答案 2 :(得分:2)

使用枚举器的另一种方法:

module Enumerable
  def split_if
    enum = each

    result = []
    tmp = [enum.peek]
    loop do
      v1, v2 = enum.next, enum.peek
      if yield(v1, v2)
        result << tmp
        tmp = [enum.peek]
      else
        tmp << v2
      end
    end
    result
  end
end

[1,2,3,5,6,8,10].split_if {|i,j| j-i > 1}

或者:

class Array
  def split_if(&block)
    prev_element = nil
    inject([[]]) do |results, element|
      if prev_element && block.call(prev_element, element)
        results << [element]
      else
        results.last << element
      end
      prev_element = element
      results
    end
  end
end

答案 3 :(得分:2)

arr = [1,2,3,5,6,8,10]
prev = arr[0]
result = arr.slice_before { |e|
  prev, prev2 = e, prev
  e != prev2.succ
}.entries
p result

不是很原创,实际上是从Ruby文档中解脱出来的。

答案 4 :(得分:1)

只是迭代地做。

x = [1,2,3,5,6,8,10]
y = []; z = []
(1..x.length - 1).each do |i|
   y << x[i - 1]
   if x[i] != x[i-1] + 1
     z << y
     y = []
   end
end
y << x[x.length - 1]
z << y
z
# => [[1, 2, 3], [5, 6], [8], [10]]