Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: opal/opal
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 763e8603d800
Choose a base ref
...
head repository: opal/opal
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: fe38617caba1
Choose a head ref
  • 2 commits
  • 3 files changed
  • 2 contributors

Commits on Jun 10, 2015

  1. RubySpec: Array#sample

    kaiwren committed Jun 10, 2015
    Copy the full SHA
    0abe0a6 View commit details
  2. Merge pull request #929 from c42engineering/rubyspec_array_sample

    RubySpec: Array#sample
    vais committed Jun 10, 2015
    Copy the full SHA
    fe38617 View commit details
Showing with 122 additions and 32 deletions.
  1. +1 −1 CHANGELOG.md
  2. +121 −9 opal/corelib/array.rb
  3. +0 −22 spec/filters/bugs/array.rb
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@

* `String#[]` and `String#slice` implementation fully compliant with rubyspec

* `Array#product`, `Array#combination`, `Array#permutation`, `Array#values_at`, `Array#rotate` and `Array#rotate!` implementations fully compliant with rubyspec
* `Array#combination`, `Array#permutation`, `Array#product`, `Array#rotate`, `Array#rotate!`, `Array#sample` and `Array#values_at` implementations fully compliant with rubyspec

* `Module#const_get` accepts a scoped constant name

130 changes: 121 additions & 9 deletions opal/corelib/array.rb
Original file line number Diff line number Diff line change
@@ -1581,19 +1581,131 @@ def rotate!(cnt=1)
replace ary
end

def sample(n = nil)
return nil if !n && empty?
return [] if n && empty?
class SampleRandom
def initialize(rng)
@rng = rng
end

if n
(1 .. n).map {
self[rand(length)]
}
else
self[rand(length)]
def rand(size)
random = Opal.coerce_to @rng.rand(size), Integer, :to_int
raise RangeError, "random value must be >= 0" if `random < 0`
raise RangeError, "random value must be less than Array size" unless `random < size`

random
end
end

def sample(count = undefined, options = undefined)
return at Kernel.rand(`self.length`) if `count === undefined`

if `options === undefined`
if (o = Opal.coerce_to? count, Hash, :to_hash)
options = o
count = nil
else
options = nil
count = Opal.coerce_to count, Integer, :to_int
end
else
count = Opal.coerce_to count, Integer, :to_int
options = Opal.coerce_to options, Hash, :to_hash
end

if count and `count < 0`
raise ArgumentError, "count must be greater than 0"
end

rng = options[:random] if options
if rng and rng.respond_to? :rand
rng = SampleRandom.new rng
else
rng = Kernel
end

return `self[#{rng.rand(`self.length`)}]` unless count

%x{
var abandon, spin, result, i, j, k, targetIndex, oldValue;
if (count > self.length) {
count = self.length;
}
switch (count) {
case 0:
return [];
break;
case 1:
return [self[#{rng.rand(`self.length`)}]];
break;
case 2:
i = #{rng.rand(`self.length`)};
j = #{rng.rand(`self.length`)};
if (i === j) {
j = i === 0 ? i + 1 : i - 1;
}
return [self[i], self[j]];
break;
default:
if (self.length / count > 3) {
abandon = false;
spin = 0;
result = #{ Array.new(count) };
i = 1;
result[0] = #{rng.rand(`self.length`)};
while (i < count) {
k = #{rng.rand(`self.length`)};
j = 0;
while (j < i) {
while (k === result[j]) {
spin++;
if (spin > 100) {
abandon = true;
break;
}
k = #{rng.rand(`self.length`)};
}
if (abandon) { break; }
j++;
}
if (abandon) { break; }
result[i] = k;
i++;
}
if (!abandon) {
i = 0;
while (i < count) {
result[i] = self[result[i]];
i++;
}
return result;
}
}
result = self.slice();
for (var c = 0; c < count; c++) {
targetIndex = #{rng.rand(`self.length`)};
oldValue = result[c];
result[c] = result[targetIndex];
result[targetIndex] = oldValue;
}
return count === self.length ? result : #{`result`[0, count]};
}
}
end

def select(&block)
return enum_for :select unless block_given?

22 changes: 0 additions & 22 deletions spec/filters/bugs/array.rb
Original file line number Diff line number Diff line change
@@ -65,28 +65,6 @@

fails "Array#rindex rechecks the array size during iteration"

fails "Array#sample calls #rand on the Object passed by the :random key in the arguments Hash"
fails "Array#sample calls #to_hash to convert the passed Object"
fails "Array#sample calls #to_int on the Object returned by #rand"
fails "Array#sample calls #to_int on the first argument and #to_hash on the second when passed Objects"
fails "Array#sample calls #to_int to convert the count when passed an Object"
fails "Array#sample does not return the same value if the Array has unique values"
fails "Array#sample ignores an Object passed for the RNG if it does not define #rand"
fails "Array#sample raises ArgumentError when passed a negative count"
fails "Array#sample raises a RangeError if the value is equal to the Array size"
fails "Array#sample raises a RangeError if the value is less than zero"
fails "Array#sample returns at most the number of elements in the Array"
fails "Array#sample when the object returned by #rand is not a Fixnum but responds to #to_int calls #to_int on the Object"
fails "Array#sample when the object returned by #rand is not a Fixnum but responds to #to_int raises a RangeError if the value is equal to the Array size"
fails "Array#sample when the object returned by #rand is not a Fixnum but responds to #to_int raises a RangeError if the value is less than zero"
fails "Array#sample with options calls #rand on the Object passed by the :random key in the arguments Hash"
fails "Array#sample with options calls #to_hash to convert the passed Object"
fails "Array#sample with options calls #to_int on the first argument and #to_hash on the second when passed Objects"
fails "Array#sample with options ignores an Object passed for the RNG if it does not define #rand"
fails "Array#sample with options when the object returned by #rand is a Fixnum raises a RangeError if the value is equal to the Array size"
fails "Array#sample with options when the object returned by #rand is a Fixnum raises a RangeError if the value is less than zero"
fails "Array#sample with options when the object returned by #rand is a Fixnum uses the fixnum as index"

fails "Array#select returns a new array of elements for which block is true"

fails "Array#shuffle attempts coercion via #to_hash"