2017-09-03 46 views
1

我想提取/獲取所有標籤之間的元素和「:/ DESC」此數組提取從數組(距離誤差的差值)的重複元素

array = ["hello", ":desc:", "claire", "et", "concise", ":/desc:", 
     ":desc:", "claire", "caca", "concise", "test", ":/desc:"] 

讓我「:DESC」有

new_array = [[":desc:", "claire", "et", "concise", ":/desc:"], 
      [":desc:", "claire", "caca", "concise", "test", ":/desc:"]] 

我試圖

final_array = [] 

start_element = ':desc:' 
end_element = ':/desc:' 

while array.any? 
    final_array << array.slice! 
(array.find_index(start_element)..array.find_index(end_element)) 
end 

但它顯然不工作,因爲我得到一個bad value for range錯誤。

+0

難道是錯字嗎?數組元素是'「:/ desc」'但你有'end_element =「:/ desc:'' – Marco

回答

4

這裏有幾個問題。從您的示例數組中看起來像結尾元素是':/desc'而不是':/desc:'(即沒有結尾:)。儘管如此,這可能只是一個錯字。

的主要問題是,除去2片後,陣列將不會是空的(它仍將包含從第一start_element"hello"。這意味着array.any?條件仍然會當find_index(start_element)不會真找到匹配的元素在這種情況下find_index將返回nil,欲以slice!時導致no implicit conversion from nil to integer

如果你知道你的數據將始終包含start_elementend_element在配對再一個辦法是:。

while start_index = array.find_index(start_element) 
    end_index = array.find_index(end_element) 
    final_array << array.slice!(start_index..end_index) 
end 

當遇到這種類型的錯誤在未來,一些值得信賴的puts調試會有幫助,在這種情況下檢查2個索引和數組的剩餘內容:

while array.any? 
    start_index = array.find_index(start_element) 
    end_index = array.find_index(end_element) 
    puts "#{start_index}..#{end_index}" 
    final_array << array.slice!(start_index..end_index) 
    puts array.inspect 
end 

1..5 
["hello", ":desc:", "claire", "caca", "concise", "test", ":/desc"] 
1..6 
["hello"] 
.. 
TypeError: no implicit conversion from nil to integer 
from (pry):146:in `slice!' 
+0

謝謝@mikej yep其實它非常好,並且與ruby完美配合。但是當在Rails上實現時(在控制器中)它並不是。 Rails拋出一個「未定義的方法find_index'」。任何想法 ?我正確地調用數組並輸入開始和結束元素。調試器將這行代碼顯示爲一個問題:while start_index = array.find_index(start_element) 對此的任何輸入? – Goeast

+0

如果對象沒有'find_index'方法,那麼它聽起來就像你實際上沒有數組。數據來自哪裏?它是以參數形式提交的嗎?你可以添加你的控制器代碼到問題的結尾,或者發佈一個新的問題嗎? – mikej

2

也可以使用的Enumarable#slice_afterEnumarable#drop_while組合:

array.slice_after(':/desc').map { |e| e.drop_while { |i| i != ':desc:' } } 
#=> [[":desc:", "claire", "et", "concise", ":/desc"], 
# [":desc:", "claire", "caca", "concise", "test", ":/desc"]] 
+0

這假定數組具有特定的結構。如果'arr = [':/ desc',':desc','/:desc']'或'[':desc:' ':/ desc','bob',':desc:',':/ desc']'。 –

+0

@ilya這將返回:#>在rails上任何想法爲什麼? – Goeast

+1

@Goeast你使用Ruby 2.3還是更高版本? – tadman

1

我認爲開始與子陣並以":/desc"結尾,並且不包含":/desc"的其他實例。請注意,如果返回arr = [":desc:", ":desc:", ":/desc"],[a]。我對數組的結構沒有任何假設(但我沒有測試過所有的可能性)。如果做出某些假設(存在匹配的非重疊對,例如簡化是可能的。

代碼

def extract(arr, target_start, target_end) 
    arr.select { |s| (s == target_start)..(s == target_end) ? true : false }. 
     slice_when { |s,t| [s, t] == [target_end, target_start] }. 
     to_a. 
     tap { |a| a.pop unless a.last.last == target_end } 
end 

例子

target_start = ":desc:" 
target_end = ":/desc" 

arr = ["hello", ":desc:", "claire", "et", "concise", ":/desc", 
     ":desc:", "claire", "caca", "concise", "test", ":/desc"] 
extract(arr, target_start, target_end) 
    #=> [[":desc:", "claire", "et", "concise", ":/desc"], 
    # [":desc:", "claire", "caca", "concise", "test", ":/desc"]] 

arr = ["hello", ":desc:", "claire", "et", "concise", ":/desc", "wanda", 
     ":desc:", "claire", "caca", "concise", "test", ":/desc", "herb"] 
extract(arr, target_start, target_end) 
    # => [[":desc:", "claire", "et", "concise", ":/desc"], 
    #  [":desc:", "claire", "caca", "concise", "test", ":/desc"]] 

arr = ["hello", ":desc:", "claire", "et", "concise", ":/desc", 
     ":desc:", "claire", "caca", "concise", "test"] 
extract(arr, target_start, target_end) 
    #=> [[":desc:", "claire", "et", "concise", ":/desc"]] 

arr = ["hello", ":desc:", "claire", "et", "concise", ":desc:", "claire", 
     "caca", "concise", "test"] 
extract(arr, target_start, target_end) 
    #=> [] 

說明

考慮

arr = ["hello", ":desc:", "claire", "et", "concise", ":/desc", 
     ":desc:", "claire", "caca", "concise", "test"] 

target_starttarget_end如在示例中給出的。步驟如下。

b = arr.select { |s| (s == target_start)..(s == target_end) ? true : false } 
    #=> [":desc:", "claire", "et", "concise", ":/desc", ":desc:", "claire", 
    # "caca", "concise", "test"] 

該第一步驟,其利用Ruby的flip-flop operator的,返回包含除那些先於第一":desc:"arr所有元素那些每個":/desc"和跟隨第一":desc:"之間的陣列。

接下來我們使用Enumerable#slice_when(Ruby v2.2中的新增功能)來生成根據需要切片b的枚舉器,然後將該枚舉器轉換爲數組。

c = b.slice_when { |s,t| [s, t] == [target_end, target_start] } 
    #=> #<Enumerator: #<Enumerator::Generator:0x00000001dd4f18>:each> 
d = c.to_a 
    #=> [[":desc:", "claire", "et", "concise", ":/desc"], 
    # [":desc:", "claire", "caca", "concise", "test"]] 

的最後一步是去除d最後一個數組,如果它不與":/desc",這是這裏的情況下終止。我們可以使用,但不能直接返回彈出的元素,這也會導致該方法返回該值。但是,如果我們在Object#tap塊中使用它,一切都很好。

d.tap { |a| a.pop unless a.last.last == target_end } 
    #=> [[":desc:", "claire", "et", "concise", ":/desc"]]