2017-09-03 51 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"]]