2011-12-24 28 views
7

Tôi muốn lấy đầu vào như:Làm cách nào để tóm tắt mảng các số nguyên dưới dạng một dãy các dải ô?

[1,2,4,5,6,7,9,13] 

và biến nó thành một cái gì đó như sau:

[[1,2],[4,7],[9,9],[13,13]] 

Mỗi mảng tiểu đại diện cho một loạt các số nguyên.

+2

Bạn có yêu cầu nếu có code để làm điều này chưa? Bạn đang yêu cầu bởi vì bạn đang cố gắng để cuộn của riêng bạn và gặp rắc rối khi thực hiện nó? – bobbymcr

+0

Tôi đang lăn của riêng mình. Dường như luôn có những cách thú vị để thực hiện loại điều này trong Ruby. – Larsenal

+0

Theo đó các điều kiện nào là phạm vi phải được xây dựng? – cvshepherd

Trả lời

19

cách tiếp cận chức năng sử dụng Enumerable#chunk:

xs.enum_for(:chunk).with_index { |x, idx| x - idx }.map do |diff, group| 
    [group.first, group.last] 
end 
# => [[1, 2], [4, 7], [9, 9], [13, 13]] 

Cách hoạt động: khi lập chỉ mục, các yếu tố liên tiếp trong mảng có cùng x - idx, vì vậy chúng tôi sử dụng giá trị đó để đoạn (nhóm các mặt hàng liên tiếp) mảng đầu vào. Cuối cùng, chúng ta chỉ cần lấy các phần tử đầu tiên và cuối cùng của mỗi nhóm để xây dựng các cặp.

+0

Điều này trông rất đẹp. Hoàn toàn quên về phương pháp đoạn mới. – cvshepherd

+2

Và để thực hiện thêm một bước nữa, '.map {| min, max | min == max? min: min .. max} 'sẽ dẫn đến:' [1..2, 4..7, 9, 13] '. –

+0

Hoặc thay đổi '[pairs.first [0], pairs.last [0]]' thành 'pairs.first [0] .. pairs.last [0]' để lấy các dải trong tất cả các vị trí: '[1. .2, 4..7, 9..9, 13..13] '. –

0

Một cách tiếp cận

def summarize(x) 
    x.inject([]) do |acc, value| 
    if acc.last && acc.last[1] + 1 == value 
     acc.last[1] = value 
     acc 
    else 
     acc << [value,value] 
    end 
    end 
end 

Tương tự như phương pháp Larsenal nhưng sử dụng bơm để quản lý những thứ nhàm chán.

3

Hmm, tốt, nó không phải tokland's kiệt tác, nhưng tôi nghĩ rằng nó có thể là một giải pháp đơn giản tốt ...

[1,2,4,5,6,7,9,13].inject([]) do |m, v| 
    if m.last.to_a.last == v.pred 
    m[-1][-1] = v 
    else 
    m << [v, v] 
    end 
    m 
end 
4

này là gần như trực tiếp từ tài liệu enumerable#slice_before phương pháp:

ar = [1,2,4,5,6,7,9,13] 
prev = ar[0] 
ar.slice_before{|e|prev,prev2 = e,prev; prev2.succ != e}.map{|a|a.first..a.last} 
#=> [1..2, 4..7, 9..9, 13..13] 

Điều này sẽ làm việc với các ký tự, ngày tháng, bất cứ điều gì với một phương pháp .succ.

2

Một giải pháp dễ dàng hơn so với @tokland's very nice one đang sử dụng chunk_while:

xs.chunk_while { |a, b| a + 1 == b }.map do |seq| 
    [seq.first, seq.last] 
end 

Note: chunk_while đã được giới thiệu trong Ruby 2.3

Các vấn đề liên quan