理解在Ruby中展平数组

时间:2016-06-27 18:27:55

标签: ruby

我对.each_with_object在某种程度上的作用感到困惑。

例如:

("a".."c").each_with_object("") {|i,str| str << i} # => "abc"

另外:

(1..3).each_with_object(0) {|i,sum| sum += i} #=> 0 

(因为整数是不可变的)。

在阅读Ruby文档中的示例后,我就是 混淆object()内的参数实际上是什么。

关于以下flattify代码:我对*的使用感到困惑;为什么else语句只是elementelement打算做什么?

def flattify(array)
  array.each_with_object([]) do |element, flattened|
    flattened.push *(element.is_a?(Array) ? flattify(element) : element)
  end
end

4 个答案:

答案 0 :(得分:1)

此处element因此会获取每个array元素的值 所有这段代码都只是递归地将所有嵌套数组放在初始数组中 但是Ruby内置了flatten方法,它可以做同样的事情 例如

ar = [1, 2, [3, 4, [5, 6]]]
ar.flatten
#=> [1, 2, 3, 4, 5, 6]

只是与您的flattify

进行比较
flattify ar
#=> [1, 2, 3, 4, 5, 6]

答案 1 :(得分:1)

传递给object()的参数充当迭代之间的中间值的累加器。在进入每次迭代时,它将作为flattened参数传递。

* splat 运算符。它将数组转换为传递给push方法的参数列表。

答案 2 :(得分:1)

var custom_lenguajes ="";
$(document).on('pageinit', '#page-1', function() {  
    // get lenguajes. START
    $.getJSON( parser_origin + "/_country/spain/v134/lacarte.restaurants.front/alacarte/php/languages.front.php", { site: id_establecimiento()}, function(data){
        for (var i=0, len=data.length; i < len; i++) {
            console.log(data[i]);
        }
        data = data['data']; 
        data3 = data; 

        $.each(data3, function(entryIndex, entry) {
            $.each(this.lenguajes, function() { 
                 alert(this.code); // don't show nothing. ONLY TEST
                 custom_lenguajes += this.code + "\,";
                console.log(custom_lenguajes);  //show "". ONLY TEST
             }); 
        });   
     });
});

答案 3 :(得分:0)

  

#each_with_object做什么相混淆

如果您先查看#each_with_object,您可能会更好地理解#inject#each_with_object#inject类似。 http://blog.krishnaswamy.in/blog/2012/02/04/ruby-inject-vs-each-with-object/中的示例,包括:

#using inject
[[:tom,25],[:jerry,15]].inject({}) do |result, name_and_age|
  name, age = name_and_age
  result[name] = age
  result
end

=> {:tom=>25, :jerry=>15}


#using each_with_object
[[:tom,25],[:jerry,15]].each_with_object({}) do |name_and_age, result|
  name, age = name_and_age
  result[name] = age
end

=> {:tom=>25, :jerry=>15}

请参阅此要点以获取示例测试:https://gist.github.com/cupakromer/3371003

深度文章:http://engineering-blog.alphasights.com/tap-inject-and-each_with_object/

<强>更新

  

#inject而不是#each_with_object在此展平代码中工作吗?

是的,见下文。我已经说明性地重构了您的展平代码以使用#inject。另外,我删除了对&#34; splat&#34;的依赖。运算符(http://ruby-doc.org/core-2.3.1/doc/syntax/calling_methods_rdoc.html#label-Array+to+Arguments+Conversion

# Flattens nested array; uses `Enumerable#inject`
# @see http://ruby-doc.org/core-2.3.1/Enumerable.html#method-i-inject
# @param arg [Array] contains objects of any type including any amount of nested arrays.
# @raise [StandardError] if arg is not Array class
# @return [Array] flat array comprised of elements from arg.
# @example
#   flattify([nil, [1, [:two, [3.0], {4=>5}], "6"]]) #=> [nil, 1, :two, 3.0, {4=>5}, "6"]
def flattify(arg)
  raise "arg is not Array" unless arg.is_a?(Array)

  # variable ret_var used here to illustrate method's return in verbose fasion
  # supplied [] used as initial value for flattened_array
  ret_var = arg.inject([]) do |flattened_array, element|
    # check if element class is Array
    if element.is_a?(Array)
      # Array#concat because flattify returns Array
      # same as: a = a + b
      # same as: a += b
      flattened_array.concat(
        # recursively call flattify with element as arg
        # element is an Array
        flattify(element)
      )
    else
      # Array#push because element is not an Array
      # same as: a << b
      flattened_array.push(element)
    end

    # used in next iteration as value for first arg above in: "|flattened_array, element|"
    # OR returned on last iteration, becoming value of ret_var above
    flattened_array
  end

  # explicit return for illustrative purposes
  return ret_var
end

更新2

  

可能[我]问为什么在这里使用splat运算符?我还是有点   对此感到困惑。似乎代码每次都在循环播放   它在扁平数组中,是什么?*?

flattened.push *(element.is_a?(Array) ? flattify(element) : element)

上述块是一个&#34;三元操作&#34; (参见:https://en.wikipedia.org/wiki/Ternary_operation),这里解释:https://stackoverflow.com/a/4252945/1076207如此:

if_this_is_a_true_value ? then_the_result_is_this : else_it_is_this

flattify示例相互比较:

# each_with_object
      flattened.push *(flattify(element))
# inject
flattened_array.concat(flattify(element))

此处* splat运算符(请参阅:https://stackoverflow.com/search?q=%5Bruby%5D+splat)与Array#concat执行相同的操作。但是,splat允许flattened.push接受三元操作返回的两种可能类型之一:1)数组;或2)无论element是什么。有关说明,请注意splat运算符如何阻止嵌套:

# each_with_object with splat
      flattened = [1,2,3]
      flattened.push *([4,5,6])   # => [1, 2, 3, 4, 5, 6]
      flattened.push *(7)         # => [1, 2, 3, 4, 5, 6, 7]

# each_with_object without splat
      flattened = [1,2,3]
      flattened.push  ([4,5,6])   # => [1, 2, 3, [4, 5, 6]]
      flattened.push  (7)         # => [1, 2, 3, [4, 5, 6], 7]

相反,Array#concat只会接受一个数组。如果使用相同的三元操作并返回一个元素,则会导致错误:

# inject
flattened_array = [1,2,3]
flattened_array.concat([4,5,6])   # => [1, 2, 3, 4, 5, 6]
flattened_array.concat(7)         # => TypeError: no implicit conversion of Fixnum into Array

总之,flattify的两个版本都实现了相同的结果。但是,#each_with_object使用#push,三元运算和splat运算符; #inject使用if / else语句,#concat#push

更新3

  

当我们用object([])做每个时,最后一个参数变为了   阵列。

是。它变成了一个数组,并且在整个迭代过程中继续是相同的数组,直到它被传回。

  

所以inject第一个成为数组?

是。第一个成为传入的数组,但仅用于第一次迭代,然后它被每个后续迭代的代码块结果替换。

enter image description here

  

我们的代码如何知道元素是否被定义为int和   flattened_Array是一个数组?

element.is_a?(Array) # => true or false

当元素为Array class时,此方法返回true,否则返回falsefalse表示除了包含int的数组之外的任何内容。

有关详细信息,请参阅:http://ruby-doc.org/core-2.3.1/Object.html#method-i-is_a-3F