each_slice keeps slices of length n, but in some cases I want to keep the full array, i.e. do nothing.
module MyModule
def num_slice
some_boolean_test? ? :full_array : 10 # Note : the content of some_boolean_test? in uninteresting, just assume sometimes it ca return true or false
end
end
class Foo
include MyModule
def a_method
big_array.each_slice(num_slice) do |array_slice|
# I want array_slice == big_array if num_slice returns :full_array
...
end
end
end
I could write a wrapper around Array#each_slice instead so I could define a different behaviour when the parameter is :full_array.
Could anyone help with that?
I'd first caution against significant logic differences between environments, since either one branch is less tested or you have twice the code to maintain. But assuming good reasons for the way you're doing it, here are some options:
Pass the array
Since num_slice is making a decision about the array, it seems reasonable num_slice should get access to it.
def num_slice(arr)
some_boolean_test? ? arr.size : 10
end
Environment configuration
You're using Rails, so you can set the slice size differently in production and your other environments. In production, make it 10, and in test, make it arbitrarily large; then just use the configured value. This is nice because there's no code difference.
def a_method
big_array.each_slice(Rails.application.config.slice_size) do |array_slice|
# ...
end
end
Wrap it
I wouldn't recommend this method because it causes the most significant difference between your environments, but since you asked about it, here's a way.
def a_method
magic_slice(big_array) do |array_slice|
# ...
end
end
def magic_slice(arr, &block)
if some_boolean_test?
block.call(arr)
else
arr.each_slice(10, &block)
end
end
def a_method(big_array, debug_context)
num_slice = debug_context ? big_array.length : 10
big_array.each_slice(num_slice) do |array_slice|
# array_slice will equal to big_array if debug_context == true
puts array_slice.inspect
end
end
test:
a_method([1,2,3,4,5], true)
[1, 2, 3, 4, 5]
Related
I'm looking to find a way to take an array in ruby, two indices in that array and return an enumerable object which will yield, in order, all the elements between and including the two indices. But for performance reasons, I want to do this subject to the following two conditions:
This slice to enum does not create a copy of the subarray I want a return an enum over. This rules out array[i..j].to_enum, for example because array[i..j] is creating a new array.
It's not necessary to loop over the entire array to create the enum.
I'm wondering if there's a way to do this using the standard library's enumerable or array functionality without having to explicitly create my own custom enumerator.
What I'm looking for is a cleaner way to create the below enumerator:
def enum_slice(array, i, j)
Enumerator.new do |y|
while i <= j
y << array[i] # this is confusing syntax for yield (see here: https://ruby-doc.org/core-2.6/Enumerator.html#method-c-new)
i += 1
end
end
end
That seems pretty reasonable, and could even be turned into an extension to Array itself:
module EnumSlice
def enum_slice(i, j)
Enumerator.new do |y|
while i <= j
y << self[i]
i += 1
end
end
end
end
Now within the Enumerator block, y represents a Proc you call when you have more data. If that block ends it's presumed you're done enumerating. There's no requirement to ever terminate, an infinite Enumerator is allowed, and in that case it's up to the caller to stop iterating.
So in other words, the y block argument can be called zero or more times, and each time it's called output is "emitted" from the enumerator. When that block exits the enumerator is considered done and is closed out, y is invalid at that point.
All y << x does is call the << method on Enumerator::Yielder, which is a bit of syntactical sugar to avoid having to do y.call(x) or y[x], both of which look kind of ugly.
Now you can add this to Array:
Array.include(EnumSlice)
Where now you can do stuff like this:
[ 1, 2, 3, 4, 5, 6 ].enum_slice(2, 4).each do |v|
p v
end
Giving you the correct output.
It's worth noting that despite having gone through all this work, this really doesn't save you any time. There's already built-in methods for this. Your enum_slice(a, i, j) method is equivalent to:
a.drop(i).take(j)
Is that close in terms of performance? A a quick benchmark can help test that theory:
require 'benchmark'
Benchmark.bm do |bm|
count = 10000
a = (0..100_000).to_a
bm.report(:enum_slice) do
count.times do
a.enum_slice(50_000, 25_000).each do
end
end
end
bm.report(:drop_take) do
count.times do
a.drop(50_000).take(25_000).each do
end
end
end
end
The results are:
user system total real
enum_slice 0.020536 0.000200 0.020736 ( 0.020751)
drop_take 7.682218 0.019815 7.702033 ( 7.720876)
So your approach is about 374x faster. Not bad!
My task:
Create 2 class objects. Delete all elements in the first object that are contained in the second.
My implementation:
class Vector
def initialize
#vector = Array.new
end
end
vector1 = Vector.new(1,2,3,4,5,6,7)
vector2 = Vector.new(3,6,7)
But this does not work as needed. What should I fix?
I think it is some sort of question from institute, and requirements are different than what you coded, I am just trying to correct your coding and rectify so you get some result, than you can further proceed to full-fill your requirements.
Your first mistake is you are asking for one parameter but providing
commas is giving multiple parameters, so I have changed it to get
array.
As changed array so now you can't add another array in first, as it
is just one array so I am removing << and adding = in initialize
method
Your initialize method is not taking any parameter, but you are
providing, but you have to define a method set_vector so you have
to use that method instead of class, as per your requirement that is
what you want.
You should define - method but than you have to play with class
variables too, as you are playing with two object and with in your
class you have to define - function, but I am here just using your
get_vector method to compare arrays and return your response.
changing put to print so you can have variables instead of all items.
Hope this will give you some helpful insight to coding.
class Vector
def initialize
#vector = Array.new
end
def set_vector=(vector)
#vector = vector
end
def get_vector
#vector
end
end
def confront(first_vector, second_vector)
first_vector.get_vector - second_vector.get_vector
end
first_vector = Vector.new
first_vector.set_vector=[1,2,3,4,5,6,7]
second_vector = Vector.new
second_vector.set_vector=[3,6,7]
final_vector = confront(first_vector, second_vector)
begin
puts "First:"
print first_vector.get_vector
puts "\nSecond:"
print second_vector.get_vector
puts "\nFinal Vector:"
print final_vector
end
I'm trying to turn 2 lines of ruby code into 1. For example:
def average(numbers)
result = numbers.compact
numbers.reduce(+) / numbers.length
end
I've been looking through array methods and can't find an appropriate one to turn this function into a one-liner. I had hoped something like this would work:
def average(numbers)
numbers.compact.<tap or another method> { |arr| arr.reduce(+) / arr.length }
end
Basically, I'm modifying the array (in the example I have to call compact to rid nil values), so I don't have access to the array variable, and I don't want an iterator, because I don't want to call reduce(+) and length on individual elements of the array.
Does anyone have an idea of methods I could look into?
I believe you mean for your method to be the following (reduce(:+), not reduce(+) and use result rather than numbers in the second line).
def average(numbers)
result = numbers.compact
result.reduce(:+) / result.length
end
average [1,2,3]
#=> 2
If you wish the average to be a float, change the second line to
result.reduce(0.0, :+) / result.length
There are various ways to combine the two lines of the method, but I don't prefer any of them to the above. Here are a few. (I don't see how Object#tap could be used here.)
numbers.compact.reduce(:+) / numbers.compact.length
(result = numbers.compact).reduce(:+) / result.compact.length
numbers.map(&:to_i).reduce(:+) / numbers.compact.length
Note that, even if numbers can be mutated, one cannot write
numbers.compact!.reduce(:+) / numbers.length
because numbers.compact! returns nil if numbers contains no nil elements.
In Ruby v2.4+ you can use Array#sum:
result.sum / result.length
You could change the way you call average
def average(numbers)
numbers.reduce(:+) / numbers.length
end
average(num_array.compact)
How can I find the complexity of a Ruby method?
For example length? If I look at the source code, I see this:
static VALUE
rb_ary_length(VALUE ary)
{
long len = RARRAY_LEN(ary);
return LONG2NUM(len);
}
But I don't know how to read that in order to find the Big O notation.
There is no maintained list of theoretical complexities of Ruby methods. Of interest to you is minitest/benchmark, which is used like this:
require 'minitest/autorun'
require 'minitest/benchmark'
class TestFoobar < Minitest::Benchmark
def setup
#foo_arrays = ( 1 .. 10_000 ).map { |n| [ 42 ] * n }
# Because default benchmarking range is [1, 10, 100, 1_000, 10_000]
end
def bench_foo_size
assert_performance_constant 0.99 do |n| #foo_arrays[ n ].size end
end
end
If you run the above test, you can actually see that the performance of Array#size method is constant. If you change #bench_foo_size to:
def bench_foo_size
assert_performance_linear 0.99 do |n| #foo_arrays[ n ].size end
end
The test will actually fail, because Array#size is not linear, but sublinear. minitest/benchmark is flexible and you can apply it to your own code as well as to built-in assets.
It is just O(1) for the length method. The length of an array is stored in a variable and can be returned without further calculations.
How to find out? By reading the source code and analysing the implementation and algorithms.
You cannot get the relevant information from that. You have to look into the source of RARRAY_LEN and LONG2NUM.
A simple way to estimate the complexity of a method is to run benchmarks with arguments that differ along the dimension you are interested in, and plot that on a graph.
Based on a question, the user wanted to access 99999th line of a 100000 lines file without having to iterate using an eachLineclosure on the first 99998 lines. So, I had suggested that he use
file.readLines().reverse()[1] to access the 99999th line of the file.
This is logically appealing to a programmer. However, I was quite doubtful about the intricacy regarding the implementation of this method.
Is the reverse() method a mere abstraction of the complete iteration on lines that is hidden from the programmer or is it really as intelligent as to be able to iterate over as less number of lines as possible to reach the required line?
As you can see from the code, reverse() calls Collections.reverse in Java to reverse the list.
However the non-mutating code gives you another option. Using listIterator() you can get an iterator with hasPrevious and previous to walk back through the list, so if you do:
// Our list
def a = [ 1, 2, 3, 4 ]
// Get a list iterator pointing at the end
def listIterator = a.listIterator( a.size() )
// Wrap the previous calls in another iterator
def iter = [ hasNext:{ listIterator.hasPrevious() },
next:{ listIterator.previous() } ] as Iterator
We can then do:
// Check the value of 1 element from the end of the list
assert iter[ 1 ] == 3
However, all of this is an ArrayList under the covers, so it's almost certainly quicker (and easier for the code to be read) if you just do:
assert a[ 2 ] == 3
Rather than all the reversing. Though obviously, this would need profiling to make sure I'm right...
According to the "Javadoc" it simply creates a new list in reverse order:
http://groovy.codehaus.org/groovy-jdk/java/util/List.html
Unless I'm missing something, you're correct an it's not that smart to jump cursor immediately. My understanding is if it's indexed as an Array it can access it directly, but if not has to iterate all over.
Alternative might be:
file.readLines().last()[-1]
This answer:
def a= [1, 2, 3, 4]
def listIterator= a.listIterator(a.size())
def iter= [hasNext: {listIterator.hasPrevious()},
next: {listIterator.previous()}] as Iterator
assert iter[1] == 3
only works in Groovy-1.7.2 and after.
In Groovy-1.7.1, 1.7.0, the 1.7 betas, 1.6, 1.5, and back to 1.0-RC-01, it doesn't find the getAt(1) method call for the proxy. For version 1.0-RC-06 and before, java.util.HashMap cannot be cast to java.util.Iterator.