Method: Array#product

Defined in:
array.c

#product(*other_arrays) ⇒ Object #product(*other_arrays) {|combination| ... } ⇒ self

Computes all combinations of elements from all the arrays, including both self and other_arrays:

  • The number of combinations is the product of the sizes of all the arrays, including both self and other_arrays.

  • The order of the returned combinations is indeterminate.

With no block given, returns the combinations as an array of arrays:

p = [0, 1].product([2, 3])
# => [[0, 2], [0, 3], [1, 2], [1, 3]]
p.size # => 4
p = [0, 1].product([2, 3], [4, 5])
# => [[0, 2, 4], [0, 2, 5], [0, 3, 4], [0, 3, 5], [1, 2, 4], [1, 2, 5], [1, 3, 4], [1, 3,...
p.size # => 8

If self or any argument is empty, returns an empty array:

[].product([2, 3], [4, 5]) # => []
[0, 1].product([2, 3], []) # => []

If no argument is given, returns an array of 1-element arrays, each containing an element of self:

a.product # => [[0], [1], [2]]

With a block given, calls the block with each combination; returns self:

p = []
[0, 1].product([2, 3]) {|combination| p.push(combination) }
p # => [[0, 2], [0, 3], [1, 2], [1, 3]]

If self or any argument is empty, does not call the block:

[].product([2, 3], [4, 5]) {|combination| fail 'Cannot happen' }
# => []
[0, 1].product([2, 3], []) {|combination| fail 'Cannot happen' }
# => [0, 1]

If no argument is given, calls the block with each element of self as a 1-element array:

p = []
[0, 1].product {|combination| p.push(combination) }
p # => [[0], [1]]

Related: see Methods for Combining.

Overloads:

  • #product(*other_arrays) {|combination| ... } ⇒ self

    Yields:

    Returns:

    • (self)


7532
7533
7534
7535
7536
7537
7538
7539
7540
7541
7542
7543
7544
7545
7546
7547
7548
7549
7550
7551
7552
7553
7554
7555
7556
7557
7558
7559
7560
7561
7562
7563
7564
7565
7566
7567
7568
7569
7570
7571
7572
7573
7574
7575
7576
7577
7578
7579
7580
7581
7582
7583
7584
7585
7586
7587
7588
7589
7590
7591
7592
7593
7594
7595
7596
7597
7598
7599
7600
7601
7602
7603
7604
7605
7606
7607
7608
7609
7610
7611
7612
7613
7614
7615
7616
7617
7618
# File 'array.c', line 7532

static VALUE
rb_ary_product(int argc, VALUE *argv, VALUE ary)
{
    int n = argc+1;    /* How many arrays we're operating on */
    volatile VALUE t0 = rb_ary_hidden_new(n);
    volatile VALUE t1 = Qundef;
    VALUE *arrays = RARRAY_PTR(t0); /* The arrays we're computing the product of */
    int *counters = ALLOCV_N(int, t1, n); /* The current position in each one */
    VALUE result = Qnil;      /* The array we'll be returning, when no block given */
    long i,j;
    long resultlen = 1;

    RBASIC_CLEAR_CLASS(t0);

    /* initialize the arrays of arrays */
    ARY_SET_LEN(t0, n);
    arrays[0] = ary;
    for (i = 1; i < n; i++) arrays[i] = Qnil;
    for (i = 1; i < n; i++) arrays[i] = to_ary(argv[i-1]);

    /* initialize the counters for the arrays */
    for (i = 0; i < n; i++) counters[i] = 0;

    /* Otherwise, allocate and fill in an array of results */
    if (rb_block_given_p()) {
        /* Make defensive copies of arrays; exit if any is empty */
        for (i = 0; i < n; i++) {
            if (RARRAY_LEN(arrays[i]) == 0) goto done;
            arrays[i] = ary_make_shared_copy(arrays[i]);
        }
    }
    else {
        /* Compute the length of the result array; return [] if any is empty */
        for (i = 0; i < n; i++) {
            long k = RARRAY_LEN(arrays[i]);
            if (k == 0) {
                result = rb_ary_new2(0);
                goto done;
            }
            if (MUL_OVERFLOW_LONG_P(resultlen, k))
                rb_raise(rb_eRangeError, "too big to product");
            resultlen *= k;
        }
        result = rb_ary_new2(resultlen);
    }
    for (;;) {
        int m;
        /* fill in one subarray */
        VALUE subarray = rb_ary_new2(n);
        for (j = 0; j < n; j++) {
            rb_ary_push(subarray, rb_ary_entry(arrays[j], counters[j]));
        }

        /* put it on the result array */
        if (NIL_P(result)) {
            FL_SET(t0, RARRAY_SHARED_ROOT_FLAG);
            rb_yield(subarray);
            if (!FL_TEST(t0, RARRAY_SHARED_ROOT_FLAG)) {
                rb_raise(rb_eRuntimeError, "product reentered");
            }
            else {
                FL_UNSET(t0, RARRAY_SHARED_ROOT_FLAG);
            }
        }
        else {
            rb_ary_push(result, subarray);
        }

        /*
         * Increment the last counter.  If it overflows, reset to 0
         * and increment the one before it.
         */
        m = n-1;
        counters[m]++;
        while (counters[m] == RARRAY_LEN(arrays[m])) {
            counters[m] = 0;
            /* If the first counter overflows, we are done */
            if (--m < 0) goto done;
            counters[m]++;
        }
    }

done:
    ALLOCV_END(t1);

    return NIL_P(result) ? ary : result;
}