enum.rb 17.8 KB
Newer Older
1 2 3
##
# Enumerable
#
skandhas's avatar
skandhas committed
4
module Enumerable
skandhas's avatar
skandhas committed
5 6 7 8 9 10 11 12 13
  ##
  # call-seq:
  #    enum.drop(n)               -> array
  #
  # Drops first n elements from <i>enum</i>, and returns rest elements
  # in an array.
  #
  #    a = [1, 2, 3, 4, 5, 0]
  #    a.drop(3)             #=> [4, 5, 0]
14

skandhas's avatar
skandhas committed
15
  def drop(n)
16
    raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
skandhas's avatar
skandhas committed
17 18
    raise ArgumentError, "attempt to drop negative size" if n < 0

19
    n = n.to_int
skandhas's avatar
skandhas committed
20
    ary = []
21
    self.each {|*val| n == 0 ? ary << val.__svalue : n -= 1 }
skandhas's avatar
skandhas committed
22 23
    ary
  end
skandhas's avatar
skandhas committed
24 25 26 27

  ##
  # call-seq:
  #    enum.drop_while {|arr| block }   -> array
Jun Hiroe's avatar
Jun Hiroe committed
28
  #    enum.drop_while                  -> an_enumerator
skandhas's avatar
skandhas committed
29 30 31 32 33
  #
  # Drops elements up to, but not including, the first element for
  # which the block returns +nil+ or +false+ and returns an array
  # containing the remaining elements.
  #
Jun Hiroe's avatar
Jun Hiroe committed
34 35
  # If no block is given, an enumerator is returned instead.
  #
skandhas's avatar
skandhas committed
36 37
  #    a = [1, 2, 3, 4, 5, 0]
  #    a.drop_while {|i| i < 3 }   #=> [3, 4, 5, 0]
38

skandhas's avatar
skandhas committed
39
  def drop_while(&block)
40
    return to_enum :drop_while unless block
Jun Hiroe's avatar
Jun Hiroe committed
41

skandhas's avatar
skandhas committed
42
    ary, state = [], false
43 44 45
    self.each do |*val|
      state = true if !state and !block.call(*val)
      ary << val.__svalue if state
skandhas's avatar
skandhas committed
46 47 48
    end
    ary
  end
49 50 51 52 53 54 55 56 57

  ##
  # call-seq:
  #    enum.take(n)               -> array
  #
  # Returns first n elements from <i>enum</i>.
  #
  #    a = [1, 2, 3, 4, 5, 0]
  #    a.take(3)             #=> [1, 2, 3]
58

59
  def take(n)
60
    raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
61 62
    i = n.to_int
    raise ArgumentError, "attempt to take negative size" if i < 0
63
    ary = []
64
    return ary if i == 0
65 66
    self.each do |*val|
      ary << val.__svalue
67 68
      i -= 1
      break if i == 0
69 70 71 72 73 74 75
    end
    ary
  end

  ##
  # call-seq:
  #    enum.take_while {|arr| block }   -> array
Jun Hiroe's avatar
Jun Hiroe committed
76
  #    enum.take_while                  -> an_enumerator
77 78 79 80
  #
  # Passes elements to the block until the block returns +nil+ or +false+,
  # then stops iterating and returns an array of all prior elements.
  #
81
  # If no block is given, an enumerator is returned instead.
82
  #
83 84
  #     a = [1, 2, 3, 4, 5, 0]
  #     a.take_while {|i| i < 3 }   #=> [1, 2]
85
  #
86
  def take_while(&block)
87
    return to_enum :take_while unless block
Jun Hiroe's avatar
Jun Hiroe committed
88

89
    ary = []
90 91 92
    self.each do |*val|
      return ary unless block.call(*val)
      ary << val.__svalue
93 94 95
    end
    ary
  end
Nobuyoshi Nakada's avatar
Nobuyoshi Nakada committed
96

skandhas's avatar
skandhas committed
97 98 99 100
  ##
  # Iterates the given block for each array of consecutive <n>
  # elements.
  #
101 102 103
  # @return [nil]
  #
  # @example
skandhas's avatar
skandhas committed
104 105 106 107 108 109 110 111 112 113
  #     (1..10).each_cons(3) {|a| p a}
  #     # outputs below
  #     [1, 2, 3]
  #     [2, 3, 4]
  #     [3, 4, 5]
  #     [4, 5, 6]
  #     [5, 6, 7]
  #     [6, 7, 8]
  #     [7, 8, 9]
  #     [8, 9, 10]
114

skandhas's avatar
skandhas committed
115
  def each_cons(n, &block)
116
    raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
skandhas's avatar
skandhas committed
117 118
    raise ArgumentError, "invalid size" if n <= 0

119
    return to_enum(:each_cons,n) unless block
skandhas's avatar
skandhas committed
120
    ary = []
121
    n = n.to_int
122
    self.each do |*val|
skandhas's avatar
skandhas committed
123
      ary.shift if ary.size == n
124
      ary << val.__svalue
skandhas's avatar
skandhas committed
125 126 127 128
      block.call(ary.dup) if ary.size == n
    end
  end

skandhas's avatar
skandhas committed
129 130 131
  ##
  # Iterates the given block for each slice of <n> elements.
  #
132 133 134
  # @return [nil]
  #
  # @example
skandhas's avatar
skandhas committed
135 136 137 138 139 140
  #     (1..10).each_slice(3) {|a| p a}
  #     # outputs below
  #     [1, 2, 3]
  #     [4, 5, 6]
  #     [7, 8, 9]
  #     [10]
141

skandhas's avatar
skandhas committed
142
  def each_slice(n, &block)
143
    raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
skandhas's avatar
skandhas committed
144 145
    raise ArgumentError, "invalid slice size" if n <= 0

146
    return to_enum(:each_slice,n) unless block
skandhas's avatar
skandhas committed
147
    ary = []
148
    n = n.to_int
149 150
    self.each do |*val|
      ary << val.__svalue
skandhas's avatar
skandhas committed
151 152 153 154 155 156
      if ary.size == n
        block.call(ary)
        ary = []
      end
    end
    block.call(ary) unless ary.empty?
157
    nil
skandhas's avatar
skandhas committed
158 159
  end

skandhas's avatar
skandhas committed
160 161 162
  ##
  # call-seq:
  #    enum.group_by {| obj | block }  -> a_hash
Jun Hiroe's avatar
Jun Hiroe committed
163
  #    enum.group_by                   -> an_enumerator
skandhas's avatar
skandhas committed
164 165 166 167 168
  #
  # Returns a hash, which keys are evaluated result from the
  # block, and values are arrays of elements in <i>enum</i>
  # corresponding to the key.
  #
169
  #     (1..6).group_by {|i| i%3}   #=> {0=>[3, 6], 1=>[1, 4], 2=>[2, 5]}
170
  #
skandhas's avatar
skandhas committed
171
  def group_by(&block)
172
    return to_enum :group_by unless block
Jun Hiroe's avatar
Jun Hiroe committed
173

skandhas's avatar
skandhas committed
174
    h = {}
175 176 177 178
    self.each do |*val|
      key = block.call(*val)
      sv = val.__svalue
      h.key?(key) ? (h[key] << sv) : (h[key] = [sv])
skandhas's avatar
skandhas committed
179 180 181 182
    end
    h
  end

183 184 185
  ##
  # call-seq:
  #    enum.sort_by { |obj| block }   -> array
Jun Hiroe's avatar
Jun Hiroe committed
186
  #    enum.sort_by                   -> an_enumerator
187 188 189
  #
  # Sorts <i>enum</i> using a set of keys generated by mapping the
  # values in <i>enum</i> through the given block.
Jun Hiroe's avatar
Jun Hiroe committed
190 191
  #
  # If no block is given, an enumerator is returned instead.
192

193
  def sort_by(&block)
194
    return to_enum :sort_by unless block
Jun Hiroe's avatar
Jun Hiroe committed
195

196
    ary = []
Nobuyoshi Nakada's avatar
Nobuyoshi Nakada committed
197
    orig = []
198 199 200 201 202 203 204 205 206 207 208 209
    self.each_with_index{|e, i|
      orig.push(e)
      ary.push([block.call(e), i])
    }
    if ary.size > 1
      __sort_sub__(ary, ::Array.new(ary.size), 0, 0, ary.size - 1) do |a,b|
        a <=> b
      end
    end
    ary.collect{|e,i| orig[i]}
  end

210 211 212 213 214 215 216 217 218
  NONE = Object.new
  ##
  # call-seq:
  #    enum.first       ->  obj or nil
  #    enum.first(n)    ->  an_array
  #
  # Returns the first element, or the first +n+ elements, of the enumerable.
  # If the enumerable is empty, the first form returns <code>nil</code>, and the
  # second form returns an empty array.
ksss's avatar
ksss committed
219 220 221
  def first(*args)
    case args.length
    when 0
222 223
      self.each do |*val|
        return val.__svalue
224 225
      end
      return nil
ksss's avatar
ksss committed
226 227
    when 1
      n = args[0]
ksss's avatar
ksss committed
228 229 230 231 232
      raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
      i = n.to_int
      raise ArgumentError, "attempt to take negative size" if i < 0
      ary = []
      return ary if i == 0
233
      self.each do |*val|
ksss's avatar
ksss committed
234 235 236
        ary << val.__svalue
        i -= 1
        break if i == 0
237
      end
ksss's avatar
ksss committed
238
      ary
ksss's avatar
ksss committed
239 240
    else
      raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 0..1)"
241 242
    end
  end
Jun Hiroe's avatar
Jun Hiroe committed
243

Jun Hiroe's avatar
Jun Hiroe committed
244 245 246 247 248 249 250 251 252 253
  ##
  # call-seq:
  #    enum.count                 -> int
  #    enum.count(item)           -> int
  #    enum.count { |obj| block } -> int
  #
  # Returns the number of items in +enum+ through enumeration.
  # If an argument is given, the number of items in +enum+ that
  # are equal to +item+ are counted.  If a block is given, it
  # counts the number of elements yielding a true value.
254
  def count(v=NONE, &block)
Jun Hiroe's avatar
Jun Hiroe committed
255 256
    count = 0
    if block
257 258
      self.each do |*val|
        count += 1 if block.call(*val)
Jun Hiroe's avatar
Jun Hiroe committed
259 260
      end
    else
261
      if v == NONE
Jun Hiroe's avatar
Jun Hiroe committed
262 263
        self.each { count += 1 }
      else
264
        self.each do |*val|
Nobuyoshi Nakada's avatar
Nobuyoshi Nakada committed
265
          count += 1 if val.__svalue == v
Jun Hiroe's avatar
Jun Hiroe committed
266 267 268 269 270
        end
      end
    end
    count
  end
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286

  ##
  # call-seq:
  #    enum.flat_map       { |obj| block } -> array
  #    enum.collect_concat { |obj| block } -> array
  #    enum.flat_map                       -> an_enumerator
  #    enum.collect_concat                 -> an_enumerator
  #
  # Returns a new array with the concatenated results of running
  # <em>block</em> once for every element in <i>enum</i>.
  #
  # If no block is given, an enumerator is returned instead.
  #
  #    [1, 2, 3, 4].flat_map { |e| [e, -e] } #=> [1, -1, 2, -2, 3, -3, 4, -4]
  #    [[1, 2], [3, 4]].flat_map { |e| e + [100] } #=> [1, 2, 100, 3, 4, 100]
  def flat_map(&block)
287
    return to_enum :flat_map unless block
288 289

    ary = []
290 291
    self.each do |*e|
      e2 = block.call(*e)
292
      if e2.respond_to? :each
293
        e2.each {|e3| ary.push(e3) }
294 295 296 297 298 299 300
      else
        ary.push(e2)
      end
    end
    ary
  end
  alias collect_concat flat_map
Jun Hiroe's avatar
Jun Hiroe committed
301 302 303 304 305 306 307 308 309 310 311

  ##
  # call-seq:
  #    enum.max_by {|obj| block }      -> obj
  #    enum.max_by                     -> an_enumerator
  #
  # Returns the object in <i>enum</i> that gives the maximum
  # value from the given block.
  #
  # If no block is given, an enumerator is returned instead.
  #
312
  #    %w[albatross dog horse].max_by {|x| x.length }   #=> "albatross"
313

Jun Hiroe's avatar
Jun Hiroe committed
314
  def max_by(&block)
315
    return to_enum :max_by unless block
Jun Hiroe's avatar
Jun Hiroe committed
316 317 318 319 320

    first = true
    max = nil
    max_cmp = nil

321
    self.each do |*val|
Jun Hiroe's avatar
Jun Hiroe committed
322
      if first
323
        max = val.__svalue
324
        max_cmp = block.call(*val)
Jun Hiroe's avatar
Jun Hiroe committed
325 326
        first = false
      else
327
        if (cmp = block.call(*val)) > max_cmp
328
          max = val.__svalue
Jun Hiroe's avatar
Jun Hiroe committed
329 330 331 332 333 334
          max_cmp = cmp
        end
      end
    end
    max
  end
335 336 337 338 339 340 341 342 343 344 345 346

  ##
  # call-seq:
  #    enum.min_by {|obj| block }      -> obj
  #    enum.min_by                     -> an_enumerator
  #
  # Returns the object in <i>enum</i> that gives the minimum
  # value from the given block.
  #
  # If no block is given, an enumerator is returned instead.
  #
  #    %w[albatross dog horse].min_by {|x| x.length }   #=> "dog"
347

348
  def min_by(&block)
349
    return to_enum :min_by unless block
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368

    first = true
    min = nil
    min_cmp = nil

    self.each do |*val|
      if first
        min = val.__svalue
        min_cmp = block.call(*val)
        first = false
      else
        if (cmp = block.call(*val)) < min_cmp
          min = val.__svalue
          min_cmp = cmp
        end
      end
    end
    min
  end
Jun Hiroe's avatar
Jun Hiroe committed
369 370 371 372 373 374 375 376 377 378 379 380 381 382

  ##
  #  call-seq:
  #     enum.minmax                  -> [min, max]
  #     enum.minmax { |a, b| block } -> [min, max]
  #
  #  Returns two elements array which contains the minimum and the
  #  maximum value in the enumerable.  The first form assumes all
  #  objects implement <code>Comparable</code>; the second uses the
  #  block to return <em>a <=> b</em>.
  #
  #     a = %w(albatross dog horse)
  #     a.minmax                                  #=> ["albatross", "horse"]
  #     a.minmax { |a, b| a.length <=> b.length } #=> ["dog", "albatross"]
383

Jun Hiroe's avatar
Jun Hiroe committed
384 385 386 387 388
  def minmax(&block)
    max = nil
    min = nil
    first = true

389
    self.each do |*val|
Jun Hiroe's avatar
Jun Hiroe committed
390
      if first
391
        val = val.__svalue
Jun Hiroe's avatar
Jun Hiroe committed
392 393 394 395
        max = val
        min = val
        first = false
      else
396
        val = val.__svalue
Jun Hiroe's avatar
Jun Hiroe committed
397
        if block
398 399
          max = val if block.call(val, max) > 0
          min = val if block.call(val, min) < 0
Jun Hiroe's avatar
Jun Hiroe committed
400 401 402 403 404 405 406 407
        else
          max = val if (val <=> max) > 0
          min = val if (val <=> min) < 0
        end
      end
    end
    [min, max]
  end
Jun Hiroe's avatar
Jun Hiroe committed
408 409 410 411 412 413 414 415 416 417 418 419 420

  ##
  #  call-seq:
  #     enum.minmax_by { |obj| block } -> [min, max]
  #     enum.minmax_by                 -> an_enumerator
  #
  #  Returns a two element array containing the objects in
  #  <i>enum</i> that correspond to the minimum and maximum values respectively
  #  from the given block.
  #
  #  If no block is given, an enumerator is returned instead.
  #
  #     %w(albatross dog horse).minmax_by { |x| x.length }   #=> ["dog", "albatross"]
421

Jun Hiroe's avatar
Jun Hiroe committed
422
  def minmax_by(&block)
423
    return to_enum :minmax_by unless block
Jun Hiroe's avatar
Jun Hiroe committed
424

Jun Hiroe's avatar
Jun Hiroe committed
425 426 427 428 429 430
    max = nil
    max_cmp = nil
    min = nil
    min_cmp = nil
    first = true

431
    self.each do |*val|
Jun Hiroe's avatar
Jun Hiroe committed
432
      if first
433 434
        max = min = val.__svalue
        max_cmp = min_cmp = block.call(*val)
Jun Hiroe's avatar
Jun Hiroe committed
435 436
        first = false
     else
437 438
        if (cmp = block.call(*val)) > max_cmp
          max = val.__svalue
Jun Hiroe's avatar
Jun Hiroe committed
439 440
          max_cmp = cmp
        end
441 442
        if (cmp = block.call(*val)) < min_cmp
          min = val.__svalue
Jun Hiroe's avatar
Jun Hiroe committed
443 444 445 446 447 448
          min_cmp = cmp
        end
      end
    end
    [min, max]
  end
Jun Hiroe's avatar
Jun Hiroe committed
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463

  ##
  #  call-seq:
  #     enum.none? [{ |obj| block }]   -> true or false
  #
  #  Passes each element of the collection to the given block. The method
  #  returns <code>true</code> if the block never returns <code>true</code>
  #  for all elements. If the block is not given, <code>none?</code> will return
  #  <code>true</code> only if none of the collection members is true.
  #
  #     %w(ant bear cat).none? { |word| word.length == 5 } #=> true
  #     %w(ant bear cat).none? { |word| word.length >= 4 } #=> false
  #     [].none?                                           #=> true
  #     [nil, false].none?                                 #=> true
  #     [nil, true].none?                                  #=> false
464

Jun Hiroe's avatar
Jun Hiroe committed
465
  def none?(&block)
466 467
    if block
      self.each do |*val|
468
        return false if block.call(*val)
469 470 471
      end
    else
      self.each do |*val|
472 473
        return false if val.__svalue
      end
Jun Hiroe's avatar
Jun Hiroe committed
474 475 476
    end
    true
  end
Jun Hiroe's avatar
Jun Hiroe committed
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493

  ##
  #  call-seq:
  #    enum.one? [{ |obj| block }]   -> true or false
  #
  # Passes each element of the collection to the given block. The method
  # returns <code>true</code> if the block returns <code>true</code>
  # exactly once. If the block is not given, <code>one?</code> will return
  # <code>true</code> only if exactly one of the collection members is
  # true.
  #
  #    %w(ant bear cat).one? { |word| word.length == 4 }  #=> true
  #    %w(ant bear cat).one? { |word| word.length > 4 }   #=> false
  #    %w(ant bear cat).one? { |word| word.length < 4 }   #=> false
  #    [nil, true, 99].one?                               #=> false
  #    [nil, true, false].one?                            #=> true
  #
494

Jun Hiroe's avatar
Jun Hiroe committed
495 496
  def one?(&block)
    count = 0
497 498
    if block
      self.each do |*val|
Jun Hiroe's avatar
Jun Hiroe committed
499
        count += 1 if block.call(*val)
500 501 502 503
        return false if count > 1
      end
    else
      self.each do |*val|
Jun Hiroe's avatar
Jun Hiroe committed
504
        count += 1 if val.__svalue
505
        return false if count > 1
Jun Hiroe's avatar
Jun Hiroe committed
506 507
      end
    end
508

Jun Hiroe's avatar
Jun Hiroe committed
509 510
    count == 1 ? true : false
  end
Jun Hiroe's avatar
Jun Hiroe committed
511 512 513 514 515 516 517 518 519 520 521 522 523 524

  ##
  #  call-seq:
  #    enum.each_with_object(obj) { |(*args), memo_obj| ... }  ->  obj
  #    enum.each_with_object(obj)                              ->  an_enumerator
  #
  #  Iterates the given block for each element with an arbitrary
  #  object given, and returns the initially given object.
  #
  #  If no block is given, returns an enumerator.
  #
  #     (1..10).each_with_object([]) { |i, a| a << i*2 }
  #     #=> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
  #
525

Jun Hiroe's avatar
Jun Hiroe committed
526
  def each_with_object(obj=nil, &block)
527
    raise ArgumentError, "wrong number of arguments (0 for 1)" if obj.nil?
Jun Hiroe's avatar
Jun Hiroe committed
528

529
    return to_enum(:each_with_object, obj) unless block
Jun Hiroe's avatar
Jun Hiroe committed
530

531
    self.each {|*val| block.call(val.__svalue, obj) }
Jun Hiroe's avatar
Jun Hiroe committed
532 533
    obj
  end
Jun Hiroe's avatar
Jun Hiroe committed
534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551

  ##
  #  call-seq:
  #     enum.reverse_each { |item| block } ->  enum
  #     enum.reverse_each                  ->  an_enumerator
  #
  #  Builds a temporary array and traverses that array in reverse order.
  #
  #  If no block is given, an enumerator is returned instead.
  #
  #      (1..3).reverse_each { |v| p v }
  #
  #    produces:
  #
  #      3
  #      2
  #      1
  #
552

Jun Hiroe's avatar
Jun Hiroe committed
553
  def reverse_each(&block)
554
    return to_enum :reverse_each unless block
Jun Hiroe's avatar
Jun Hiroe committed
555

556 557 558 559 560 561
    ary = self.to_a
    i = ary.size - 1
    while i>=0
      block.call(ary[i])
      i -= 1
    end
Jun Hiroe's avatar
Jun Hiroe committed
562 563
    self
  end
Jun Hiroe's avatar
Jun Hiroe committed
564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583

  ##
  #  call-seq:
  #     enum.cycle(n=nil) { |obj| block }  ->  nil
  #     enum.cycle(n=nil)                  ->  an_enumerator
  #
  #  Calls <i>block</i> for each element of <i>enum</i> repeatedly _n_
  #  times or forever if none or +nil+ is given.  If a non-positive
  #  number is given or the collection is empty, does nothing.  Returns
  #  +nil+ if the loop has finished without getting interrupted.
  #
  #  Enumerable#cycle saves elements in an internal array so changes
  #  to <i>enum</i> after the first pass have no effect.
  #
  #  If no block is given, an enumerator is returned instead.
  #
  #     a = ["a", "b", "c"]
  #     a.cycle { |x| puts x }  # print, a, b, c, a, b, c,.. forever.
  #     a.cycle(2) { |x| puts x }  # print, a, b, c, a, b, c.
  #
584

ksss's avatar
ksss committed
585 586
  def cycle(nv = nil, &block)
    return to_enum(:cycle, nv) unless block
587

ksss's avatar
ksss committed
588 589 590 591 592 593 594
    n = nil

    if nv.nil?
      n = -1
    else
      unless nv.respond_to?(:to_int)
        raise TypeError, "no implicit conversion of #{nv.class} into Integer"
595
      end
ksss's avatar
ksss committed
596 597 598
      n = nv.to_int
      unless n.kind_of?(Integer)
        raise TypeError, "no implicit conversion of #{nv.class} into Integer"
599
      end
ksss's avatar
ksss committed
600 601
      return nil if n <= 0
    end
Jun Hiroe's avatar
Jun Hiroe committed
602

ksss's avatar
ksss committed
603 604 605 606 607 608 609 610 611 612
    ary = []
    each do |*i|
      ary.push(i)
      yield(*i)
    end
    return nil if ary.empty?

    while n < 0 || 0 < (n -= 1)
      ary.each do |i|
        yield(*i)
Jun Hiroe's avatar
Jun Hiroe committed
613 614
      end
    end
ksss's avatar
ksss committed
615 616

    nil
Jun Hiroe's avatar
Jun Hiroe committed
617
  end
Jun Hiroe's avatar
Jun Hiroe committed
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635

  ##
  #  call-seq:
  #     enum.find_index(value)          -> int or nil
  #     enum.find_index { |obj| block } -> int or nil
  #     enum.find_index                 -> an_enumerator
  #
  #  Compares each entry in <i>enum</i> with <em>value</em> or passes
  #  to <em>block</em>.  Returns the index for the first for which the
  #  evaluated value is non-false.  If no object matches, returns
  #  <code>nil</code>
  #
  #  If neither block nor argument is given, an enumerator is returned instead.
  #
  #     (1..10).find_index  { |i| i % 5 == 0 and i % 7 == 0 }  #=> nil
  #     (1..100).find_index { |i| i % 5 == 0 and i % 7 == 0 }  #=> 34
  #     (1..100).find_index(50)                                #=> 49
  #
636

Jun Hiroe's avatar
Jun Hiroe committed
637
  def find_index(val=NONE, &block)
638
    return to_enum(:find_index, val) if !block && val == NONE
Jun Hiroe's avatar
Jun Hiroe committed
639 640 641

    idx = 0
    if block
642 643
      self.each do |*e|
        return idx if block.call(*e)
Jun Hiroe's avatar
Jun Hiroe committed
644 645 646
        idx += 1
      end
    else
647 648
      self.each do |*e|
        return idx if e.__svalue == val
Jun Hiroe's avatar
Jun Hiroe committed
649 650 651 652 653
        idx += 1
      end
    end
    nil
  end
Jun Hiroe's avatar
Jun Hiroe committed
654 655 656 657 658 659 660 661 662 663 664 665

  ##
  #  call-seq:
  #     enum.zip(arg, ...)                  -> an_array_of_array
  #
  #  Takes one element from <i>enum</i> and merges corresponding
  #  elements from each <i>args</i>.  This generates a sequence of
  #  <em>n</em>-element arrays, where <em>n</em> is one more than the
  #  count of arguments.  The length of the resulting sequence will be
  #  <code>enum#size</code>.  If the size of any argument is less than
  #  <code>enum#size</code>, <code>nil</code> values are supplied.
  #
666

Jun Hiroe's avatar
Jun Hiroe committed
667 668
  def zip(*arg)
    ary = []
669
    arg = arg.map{|a|a.to_a}
Jun Hiroe's avatar
Jun Hiroe committed
670
    i = 0
671
    self.each do |*val|
Jun Hiroe's avatar
Jun Hiroe committed
672
      a = []
673
      a.push(val.__svalue)
Jun Hiroe's avatar
Jun Hiroe committed
674 675
      idx = 0
      while idx < arg.size
676
        a.push(arg[idx][i])
Jun Hiroe's avatar
Jun Hiroe committed
677 678 679 680 681 682 683
        idx += 1
      end
      ary.push(a)
      i += 1
    end
    ary
  end
684 685 686 687 688 689 690 691 692 693 694

  ##
  #  call-seq:
  #     enum.to_h  -> hash
  #
  #  Returns the result of interpreting <i>enum</i> as a list of
  #  <tt>[key, value]</tt> pairs.
  #
  #     %i[hello world].each_with_index.to_h
  #       # => {:hello => 0, :world => 1}
  #
695

696 697 698 699 700 701 702 703 704 705
  def to_h
    h = {}
    self.each do |*v|
      v = v.__svalue
      raise TypeError, "wrong element type #{v.class} (expected Array)" unless v.is_a? Array
      raise ArgumentError, "element has wrong array length (expected 2, was #{v.size})" if v.size != 2
      h[v[0]] = v[1]
    end
    h
  end
706 707 708 709

  def nil.to_h
    {}
  end
skandhas's avatar
skandhas committed
710
end