2012-12-01 24 views
47

Cho một mảng, làm thế nào tôi có thể tìm thấy tất cả các chỉ số của các phần tử khớp với một điều kiện nhất định?Tìm chỉ mục của các phần tử khớp với một điều kiện nhất định

Ví dụ, nếu tôi có:

arr = ['x', 'o', 'x', '.', '.', 'o', 'x'] 

Để tìm tất cả các chỉ số nơi mục là x, tôi có thể làm:

arr.each_with_index.map { |a, i| a == 'x' ? i : nil }.compact # => [0, 2, 6] 

hoặc

(0..arr.size-1).select { |i| arr[i] == 'x' } # => [0, 2, 6] 

Có cách tốt hơn để đạt được điều này?

+0

Bạn đang tìm kiếm cho logic hoặc cú pháp, có thể giúp bạn với logic nhưng không phải là cú pháp mặc dù :) không quen thuộc với ruby ​​nhiều ... Bạn có thể sử dụng một 'regexp' – bonCodigo

+0

Tôi đoán cả logic và cú pháp :) Không chắc chắn về regex vì các mục không phải là chuỗi cần thiết. –

+0

Đây là một trong số ít các trường hợp tôi nghĩ rằng sự hiểu biết danh sách của Python thực sự đọc tốt hơn: '[i cho i, một liệt kê (arr) nếu a == 'x']'. –

Trả lời

64

của Ruby 1.9:

arr = ['x', 'o', 'x', '.', '.', 'o', 'x'] 
p arr.each_index.select{|i| arr[i] == 'x'} # =>[0, 2, 6] 

Code

+1

Cảm ơn vì điều này. Tôi chỉ tìm thấy 'arr.enum_for (: each_with_index) .collect {| item, index | item == 'x'? chỉ mục: nil} .delete_if {| i | i.nil? } 'trước này. –

+3

@ 6ftDan 'delete_if {| i | i.nil? } '==' nhỏ gọn'. – steenslag

+0

Đẹp và thành ngữ – ki4jnq

19

Một cách khác:

arr.size.times.select {|i| arr[i] == 'x'} # => [0, 2, 6] 

EDIT:

Không chắc nếu điều này thậm chí còn cần thiết, nhưng ở đây họ đang có.

Benchmarks:

arr = 10000000.times.map{rand(1000)}; 

Benchmark.measure{arr.each_with_index.map { |a, i| a == 50 ? i : nil }.compact} 
2.090000 0.120000 2.210000 ( 2.205431) 

Benchmark.measure{(0..arr.size-1).select { |i| arr[i] == 50 }} 
1.600000 0.000000 1.600000 ( 1.604543) 

Benchmark.measure{arr.map.with_index {|a, i| a == 50 ? i : nil}.compact} 
1.810000 0.020000 1.830000 ( 1.829151) 

Benchmark.measure{arr.each_index.select{|i| arr[i] == 50}} 
1.590000 0.000000 1.590000 ( 1.584074) 

Benchmark.measure{arr.size.times.select {|i| arr[i] == 50}} 
1.570000 0.000000 1.570000 ( 1.574474) 
+3

+1 cho điểm chuẩn – Sheharyar

+0

được thăng hạng để triển khai hợp lý nhất. Mọi thứ khác phức tạp hơn. – akostadinov

10

Một cải thiện nhẹ so each_with_index.map dòng của bạn

arr.map.with_index {|a, i| a == 'x' ? i : nil}.compact # => [0, 2, 6] 
7

phương pháp này là lâu hơn một chút nhưng tăng gấp đôi càng nhanh

class Array 
    def find_each_index find 
    found, index, q = -1, -1, [] 
    while found 
     found = self[index+1..-1].index(find) 
     if found 
     index = index + found + 1 
     q << index 
     end 
    end 
    q 
    end 
end 

arr = ['x', 'o', 'x', '.', '.', 'o', 'x'] 
p arr.find_each_index 'x' 
# [0, 2, 6] 

Dưới đây là điểm chuẩn của AGS campared với giải pháp này

arr = 10000000.times.map{rand(1000)}; 

puts Benchmark.measure{arr.each_with_index.map { |a, i| a == 50 ? i : nil }.compact} 
puts Benchmark.measure{(0..arr.size-1).select { |i| arr[i] == 50 }} 
puts Benchmark.measure{arr.map.with_index {|a, i| a == 50 ? i : nil}.compact} 
puts Benchmark.measure{arr.each_index.select{|i| arr[i] == 50}} 
puts Benchmark.measure{arr.size.times.select {|i| arr[i] == 50}} 
puts Benchmark.measure{arr.find_each_index 50} 

    # 1.263000 0.031000 1.294000 ( 1.267073) 
    # 0.843000 0.000000 0.843000 ( 0.846048) 
    # 0.936000 0.015000 0.951000 ( 0.962055) 
    # 0.842000 0.000000 0.842000 ( 0.839048) 
    # 0.843000 0.000000 0.843000 ( 0.843048) 
    # 0.405000 0.000000 0.405000 ( 0.410024) 
+0

Lưu ý rằng điều này chỉ hoạt động khi sử dụng chỉ mục với tham số, không phải với một khối. Đối với một khối, giải pháp được chấp nhận vẫn là cách duy nhất. – Sprachprofi

4

Không chắc chắn nếu bạn xem xét việc này một sự cải tiến hay không, nhưng sử dụng (map + compact) như một bộ lọc cảm thấy rất clunky với tôi. Tôi sẽ sử dụng select, vì đó là những gì nó cho, và sau đó chỉ cần lấy một phần của kết quả tôi quan tâm:

arr.each_with_index.select { |a,i| a == 'x' }.map &:last 
2

tôi xác định Array#index_all mà cư xử như Array#index nhưng trả về chỉ số tất cả phù hợp. Phương pháp này có thể lấy một đối số và chặn.

class Array 
    def index_all(obj = nil) 
    if obj || block_given? 
     proc = obj ? ->(i) { self[i] == obj } : ->(i) { yield self[i] } 
     self.each_index.select(&proc) 
    else 
     self.each 
    end 
    end 
end 

require 'test/unit' 

class TestArray < Test::Unit::TestCase 
    def test_index_all 
    arr = ['x', 'o', 'x', '.', '.', 'o', 'x'] 
    result = arr.index_all('x') 
    assert_equal [0, 2, 6], result 

    arr = [100, 200, 100, 300, 100, 400] 
    result = arr.index_all {|n| n <= 200 } 
    assert_equal [0, 1, 2, 4], result 
    end 
end 
Các vấn đề liên quan