a = [1,2,3,5,6,9,10]
要得到
[1,2,3], [5,6], [9,10]
请问大家有没有比较优雅的实现
@kikyous 你分组的算法是什么?是否可以在 chunk 里面加上你分组的算法?
[3,1,4,1,5,9,2,6,5,3,5].chunk {|n|
n.even?
}.each {|even, ary|
p [even, ary]
}
#=> [false, [3, 1]]
# [true, [4]]
# [false, [1, 5, 9]]
# [true, [2, 6]]
# [false, [5, 3, 5]]
现在的写法
def calc_range list
range = []
_last = nil
list.each do |d|
if !_last or (_last + 1 == d)
range << d
else
range << nil
range << d
end
_last = d
end
range.split nil
end
pry(main)> calc_range [1,2,3,5,6,9,10]
=> [[1, 2, 3], [5, 6], [9, 10]]
改进的写法
def calc_range list
range = []
_last = nil
list.each do |d|
range << nil if _last and (_last + 1 != d)
range << d
_last = d
end
range.split nil
end
另外为啥我这运行楼主的程序显示 undefined method `split' for [1, 2, 3, nil, 5, 6, nil, 9, 10]:Array (NoMethodError) 我好像记得 string 才有 split ...
a = [1,2,3,5,6,8,9]
indexes = []
a.each_with_index do |e, i|
if a[i+1].to_i - e != 1
indexes << i
end
end
result = []
indexes.each_with_index do |e, i|
if i == 0
result << a[0..e]
else
result << a[(indexes[i - 1] + 1)..e]
end
end
puts result.inspect
@blacktulip ...
Array#split
是 activesupport 添加的。如果不用 activesupport :
def calc_range list
last = nil
flip = false
list.chunk do |e|
flip ^= !(last and e == last + 1)
last = e
flip
end.map &:last
end
calc_range [1,2,3,5,6,9,10,2,3,4]
%w(1 2 3 4 5 6 7).in_groups_of(3) {|group| p group}
["1", "2", "3"]
["4", "5", "6"]
["7", nil, nil]
%w(1 2 3).in_groups_of(2, ' ') {|group| p group}
["1", "2"]
["3", " "]
%w(1 2 3).in_groups_of(2, false) {|group| p group}
["1", "2"]
["3"]
向原数组内未相连的部分插入分隔符(比如 nil)也可以这样做:
# Suppose an array given like this
array = [1,2,3,5,6,8,9]
# Then put an delimiter
result = Range.new(array.first, array.last).map do |element|
array.include?(element) ? element : nil
end
在之后对于 result 的处理,如果没有 ActiveSupport 的扩展,那就只好手动去 chunk 了吧。
a = [1,2,3,5,6,9,10]
arr, last = [], nil
a.each do |e|
arr.last << e if c = (last and e == last + 1)
arr << [e] unless c
last = e
end
省个局部变量
a = [1,2,3,5,6,9,10]
arr = []
a.inject(nil) do |last, e|
arr.last << e if c = (last and e == last + 1)
arr << [e] unless c
e
end
触目惊心,ruby 程序之泪死活要变成一行 误
a = [1,2,3,5,6,9,10]
arr = []
a.inject(nil) { |last, e| (last && e == last + 1 ? arr.last << e : arr << [e]) and e }
one liner
a.inject([]){|memo, n| memo.size >= 1 && n - memo[-1][-1] == 1 ? memo[-1] << n : memo << [n]; memo}
非得写一行吗?来个正常的:
class Array
def slice_by(n=3)
return self if length <= n
arr = []
if length.even? && n==3
each_slice(2){|i| arr << i}
else
arr << (self[0]..self[n-1]).to_a
self[n..length-1].each_slice(2){|i| arr << i}
end
return arr
end
end
pry(main)> [1,2,3].slice_by
=> [1, 2, 3]
pry(main)> [1,2,3,4,5].slice_by
=> [[1, 2, 3], [4, 5]]
pry(main)> [1,2,3,4,5].slice_by(4)
=> [[1, 2, 3, 4], [4, 5]]
pry(main)> [1,2,3,4,5,6].slice_by
=> [[1, 2], [3, 4], [5, 6]]
pry(main)> [1,2,3,4,5,6,7,8,9,10].slice_by
=> [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]
pry(main)> [1,2,3,4,5,6,7,8,9,10].slice_by(5)
=> [[1, 2, 3, 4, 5], [6, 7], [8, 9], [10]]
优雅吗?
你还可以修改方法里的each_slice(2)
,把这个数字 2 通过传递参数改成任意你想要的。
水平不够啊,只能 Python 了
reduce(lambda l,e:l and l[-1][-1]+1==e and l[:-1]+[l[-1]+[e]] or l+[[e]],[1,2,3,5,6,9,10],[])
[1,2,3,5,6,9,10].inject([]){|l,e|(l!=[]&&l[-1][-1]+1==e)?l[0..-2]+[l[-1]+[e]]:l+[[e]]}
arr = [1,2,3,5,6,9,10]
f, l = arr.first, arr.last
[f - 1,*((f..l).to_a - arr),l + 1].each_cons(2).map{|s, e| arr.select{|n| n > s && n < e}}.select{|a|a.size > 0}
不太优雅,但是比较通用,数组的元素随便定义。
楼上的写代码,只为满足楼主那一个例子吗?a = [1,2,3,5,6,9,10]
arr = [1,2,3,7,5,6,9,10,13,14,15,16,20,15,16]
result = []
b = []
arr.each_with_index do|i, index|
b << i if (i.succ == arr[index+1]) || (i.succ != arr[index+1] && i == arr[index-1].succ)
if i.succ != arr[index+1]
result << b unless b.empty?
b = []
elsif i.succ != arr[index+1] && i == arr[index-1].succ
b << i
result << b
b = []
end
end
puts result.inspect
#=> [[1, 2, 3], [5, 6], [9, 10], [13, 14, 15, 16], [15, 16]]
你还漏了[7] [20]
吧你
[1,2,3,7,5,6,9,10,13,14,15,16,20,15,16].inject([]){|l,e|(l!=[]&&l[-1][-1]+1==e)?l[0..-2]+[l[-1]+[e]]:l+[[e]]}
=> [[1, 2, 3], [7], [5, 6], [9, 10], [13, 14, 15, 16], [20], [15, 16]]
更短的来了...
a = [1,2,3,5,6,9,10,2,3,4]
(1..a.size).zip(a).chunk{|l,r|l-r}.map{|_,e|e.map &:last}
或者长一点但简单点的:
a.each_with_index.chunk{|l,r|l-r}.map{|_,e|e.map &:first}
a.slice_before([0]){|e,l|e-1!=(l<<e).shift}.to_a
或者不清空状态
a.slice_before([]){|e,l|e-1!=(l<<e)[-2]}.to_a
易读版而且可以自定义什么为“连续”
def consecutive?(x,y)
x && y && (x-y).abs == 1
end
a.reduce([]) {|accu, num| consecutive?(accu.flatten.last, num) ? (accu.last << num; accu) : accu << [num] }
取个巧:
a = [1,2,3,5,6,8,9]
(1..9).map{|i| a.index(i) ? i : nil}.split(nil) # [[1, 2, 3], [5, 6], [8, 9]]
def seq(a)
c = a.clone
cur = [c.shift]
ret = [cur]
while (last = c.shift)
if last - cur.last == 1
cur << last
else
cur = [last]
ret << cur
end
end
ret
end