2009-09-02 26 views
7

Tôi biết toán tử || =, nhưng đừng nghĩ rằng nó sẽ giúp tôi ở đây ... cố gắng tạo một mảng đếm số "loại" trong một mảng các đối tượng.Ruby: Cách đặt biến thành 0 hoặc, nếu nó đã được đặt, tăng thêm 1

array.each do |c| 
    newarray[c.type] = newarray[c.type] ? newarray[c.type]+1 ? 0 
end 

Có cách nào duyên dáng hơn để thực hiện việc này không?

+1

Dấu hỏi thứ hai phải là dấu hai chấm. – jonas054

Trả lời

14
types = Hash.new(-1) # It feels like this should be 0, but to be 
        # equivalent to your example it needs to be -1 
array.each do |c| 
    types[c.type] += 1 
end 
+0

+1 cho nhận xét. –

2

||= không giúp đỡ:

types = {} 
array.each do |c| 
    types[c.class] ||= 0 
    types[c.class] += 1 
end 
+2

Điều đó sẽ đặt nó thành 1 lần đầu tiên thông qua ... newarray [c.type] || = -1 newarray [c.type] + = 1 –

+0

Vâng, đó không phải là những gì chúng tôi muốn? Ví dụ, nếu mảng là [1] thì các kiểu sẽ trở thành {Fixnum => 1}. Và chúng tôi có 1 Fixnum. –

+0

Chúng tôi muốn nó được đặt thành 0 ở cuối vòng lặp, không phải 1, nếu! Loại [c.class]. – Chuck

4
array.each do |c| 
    newarray[c.type] = 1 + (newarray[c.type] || -1) 
end 

Ngoài ra

array.each do |c| 
    newarray[c.type] ||= -1 
    newarray[c.type] += 1 
end 
+1

Tôi thích cái thứ hai của bạn. Hạn chế duy nhất là nó là loại điều mà ai đó đến cùng sau này có thể là một sai lầm. Nếu sử dụng nó trong sản xuất, tôi sẽ thêm một bình luận nhanh giải thích tại sao tôi làm theo cách đó. – Pesto

+0

Tôi thích cách thứ hai. Đó là hai dòng và có khả năng hai bài tập, nhưng dễ đọc hơn so với điều kiện phức tạp hoặc bất cứ điều gì đẩy trên một dòng. – Chuck

6

Sử dụng Array#fetch phương pháp mà bạn có thể cung cấp một giá trị mặc định nếu chỉ số không tồn tại:

array.each do |c| 
    newarray[c.type] = newarray.fetch(c.type, -1) + 1 
end 
0

Biến số newarray của bạn được đặt tên kỳ quặc, vì trong Ruby và hầu hết các ngôn ngữ khác, mảng được lập chỉ mục theo số nguyên, không phải các đối tượng ngẫu nhiên như Class. Có nhiều khả năng đây là Hash.

Ngoài ra, bạn nên sử dụng c.class, thay vì c.type, không được dùng nữa.

Cuối cùng, vì bạn đang tạo một Hash, bạn có thể sử dụng inject như vậy:

newarray = array.inject({}) do |h,c| 
    h[c.class] = h.key?(c.class) ? h[c.class]+1 : 0 
    h 
end 

Hoặc, đối với một lớp lót:

newarray = array.inject({}) { |h,c| h[c.class] = h.key?(c.class) ? h[c.class]+1 : 0 ; h } 

Như bạn có thể thấy, điều này mang lại kết quả mong muốn:

irb(main):001:0> array = [1, {}, 42, [], Object.new(), [1, 2, 3]] 
=> [1, {}, 42, [], #<Object:0x287030>, [1, 2, 3]] 
irb(main):002:0> newarray = array.inject({}) { |h,c| h[c.class] = h.key?(c.class) ? h[c.class]+1 : 0 ; h } 
=> {Object=>0, Hash=>0, Array=>1, Fixnum=>1} 
0

Trong Ruby 1.8.7 hoặc mới hơn, bạn có thể sử dụng group_by và sau đó chuyển từng danh sách các phần tử thành số đếm - 1 và tạo một băm từ mảng được trả về bởi map.

Hash[array.group_by(&:class).map { |k,v| [k, v.size-1] }] 
Các vấn đề liên quan