Ruby << Hash << Array.include[incremented integer] - ruby

I have the following:
ht = Hash.new {|h,k| h[k]=[]}
CertainParentClass.decendants.each do {|childrens|
ht[childrens] << incremented_integer
}
$global_switch[0 ... ht.size] = false
def check_switch(object_class)
return $global_switch[ht[object_class][0]]
end
And I'd like to know how to do incremented_integer in ruby or if there's a better solution rather than inserting an integer to check the position value, like key_position that would be great!
def check_switch(object_class)
return $global_switch[ht[object_class].key_position]
end

I think something below using Enumerable#each_with_index:
CertainParentClass.decendants.each_with_index do |childrens,ind|
ht[childrens] << ind # ind will increment with each pass by 1 default.
end
In case of #each_with_index, ind value will bet set as 0 by default. Now if you want to set it with other numbers rather than 0, use Enumerator#with_index. Like below
CertainParentClass.decendants.each.with_index(2) do |childrens,ind|
ht[childrens] << ind # ind will increment with each pass by 1 default.
end
I passed 2 as initial argument, which is the first ind value in the with_index(2) method, you can change it as per your need

You can use modified iterator :with_index of Enumerable module, which added index number to the end of block argument list, do as follows:
CertainParentClass.decendants.each.with_index do |child, index|
ht[ childrens ] << index
end
And of couse you can modify not only :each iterator method, but a method, which returns modified, or non modified enumerable. Those methods are :map, :reject, :select.

Related

How to use reduce/inject in Ruby without getting Undefined variable

When using an accumulator, does the accumulator exist only within the reduce block or does it exist within the function?
I have a method that looks like:
def my_useless_function(str)
crazy_letters = ['a','s','d','f','g','h']
str.split.reduce([]) do |new_array, letter|
for a in 0..crazy_letters.length-1
if letter == crazy_letters[a]
new_array << letter
end
end
end
return true if (new_array == new_array.sort)
end
When I execute this code I get the error
"undefined variable new_array in line 11 (the return statement)"
I also tried assigning the new_array value to another variable as an else statement inside my reduce block but that gave me the same results.
Can someone explain to me why this is happening?
The problem is that new_array is created during the call to reduce, and then the reference is lost afterwards. Local variables in Ruby are scoped to the block they are in. The array can be returned from reduce in your case, so you could use it there. However, you need to fix a couple things:
str.split does not break a string into characters in Ruby 2+. You should use str.chars, or str.split('').
The object retained for each new iteration of reduce must be retained by returning it from the block each time. The simplest way to do this is to put new_array as the last expression in your block.
Thus:
def my_useless_function(str)
crazy_letters = ['a','s','d','f','g','h']
crazy_only = str.split('').reduce([]) do |new_array, letter|
for a in 0..crazy_letters.length-1
if letter == crazy_letters[a]
new_array << letter
end
end
new_array
end
return true if (crazy_only == crazy_only.sort)
end
Note that your function is not very efficient, and not very idiomatic. Here's a shorter version of the function that is more idiomatic, but not much more efficient:
def my_useless_function(str)
crazy_letters = %w[a s d f g h]
crazy_only = str.chars.select{ |c| crazy_letters.include?(c) }
crazy_only == crazy_only.sort # evaluates to true or false
end
And here's a version that's more efficient:
def efficient_useless(str)
crazy_only = str.scan(/[asdfgh]/) # use regex to search for the letters you want
crazy_only == crazy_only.sort
end
Block local variables
new_array doesn't exist outside the block of your reduce call. It's a "block local variable".
reduce does return an object, though, and you should use it inside your method.
sum = [1, 2, 3].reduce(0){ |acc, elem| acc + elem }
puts sum
# 6
puts acc
# undefined local variable or method `acc' for main:Object (NameError)
Your code
Here's the least amount of change for your method :
def my_useless_function(str)
crazy_letters = ['a','s','d','f','g','h']
new_array = str.split(//).reduce([]) do |new_array, letter|
for a in 0..crazy_letters.length-1
if letter == crazy_letters[a]
new_array << letter
end
end
new_array
end
return true if (new_array == new_array.sort)
end
Notes:
return isn't needed at the end.
true if ... isn't needed either
for loop should never be used in Ruby
reduce returns the result of the last expression inside the block. It was for in your code.
If you always need to return the same object in reduce, it might be a sign you could use each_with_object.
"test".split is just ["test"]
String and Enumerable have methods that could help you. Using them, you could write a much cleaner and more efficient method, as in #Phrogz answer.

Ruby array initialize on read

I would like to be able to do this:
my_array = Array.new
my_array[12] += 1
In other words, somehow upon trying to access entry 12, finding it uninitialized, it is initialized to zero so I can add one to it. Array.new has a default: parameter, but that comes into play when you initialize the array with a known number of slots. Other than writing my own class, is there a ruby-ish way of doing this?
No need to create a new class :
my_hash = Hash.new(0)
my_hash[12] += 1
p my_hash
#=> {12=>1}
For many cases, hashes and arrays can be used interchangeably.
An array with an arbitrary number of elements and a default value sounds like a hash to me ;)
Just to make it clear : Hash and Array aren't equivalent. There will be cases where using a hash instead of an array will be completely wrong.
Something like:
a[12] = (a[12] ||= 0) + 1
Making use of nil.to_i == 0
my_array = Array.new
my_array[12] = my_array[12].to_i + 1
Note, that unlike other solutions here so far, this one works for any arbitrary initial value.
my_array = Array.new.extend(Module.new {
def [] idx
super || 0
end
})
my_array[12] += 1
#⇒ 1
This is not possible with the stock Array::new method.
https://docs.ruby-lang.org/en/2.0.0/Array.html#method-c-new
You will either need to monkey patch Array class, or monkey patch nil class. And they are not recommended.
If you have a specific use case, I would create a new wrapper class around Array
class MyArray < Array
def [](i)
super(i) ? super(i) : self[i] = 0
end
end
arr = MyArray.new
arr[12] += 1 # => 1

How do you create an enumerable object that evaluates on demand out of another enumerable object?

Given I have code like the following, what do I need to do to make it work?
config = {} #options for faster csv
input_file = "foo.csv"
# can be in any class or module
def count_item_groups(items)
results = Hash.new(0)
(items || []).each do |current|
results[current.to_s] += 1
end
results
end
row_value_iterator = FasterCSV.foreach(input_file, config) do |row|
yield return row[1]
end
result = count_item_groups(row_value_iterator)
Versus code like this
def do_it_all
results = Hash.new(0)
FasterCSV.foreach(input_file, config) do |row|
results[row[1].to_s] += 1
end
results
end
Result should be a hash with keys of the row[1] values. yield return doesn't exist in Ruby, but I'm sure that Ruby can handle this type of code.
That's what I understand you are asking: "How can I transform a method like FasterCSV.foreach that works imperatively (by doing side-effects) to something functional (that yields values) so I can modularize my code".
Answer: In Ruby you can transform a each method to an Enumerator object with Object#enum_for. Now you could use your count_item_groups with the output of the map, but I'd suggest to use Facets' Enumerable#frequency:
results = FasterCSV.enum_for(:foreach, "file.csv", {}).map do |row|
row[1].to_s
end.frequency
#=> {"val1"=>3, "val2"=>1}
I'm not sure what you're asking, I assumed that is related to chainable feature.
Instead of passing the object iterator to another iterator as parameter, in ruby you can chain these iterators. It mignt look like this.
row_value_iterator = FasterCSV.foreach(input_file, config).map do |row|
row[1]
end
result = row_value_iterator.each_with_object(Hash.new(0)) do |current,results|
results[current.to_s] += 1
end
Or do it in truly chain style:
result = FasterCSV.foreach(input_file,config).each_with_object(Hash.new(0)) do |row,results|
results[row[1].to_s] += 1
end

In Ruby, how can I collect each new element passing through a method into an array?

I'm creating a small prime number program, and am confused about one thing.
I have a function called create_numbers, that generates numbers and passes them to a new function called check_for_primes, which passes only prime numbers to a final function called count_primes. I want to collect each prime into an array in the function count_primes, but for some reason each number is collected as its own array.
Any idea of what I'm doing wrong?
Here is the code:
def create_numbers
nums = 1
while nums < 100
nums = nums + 2
check_for_primes(nums)
end
end
def count_primes(nums)
array = []
array << nums
puts array.inspect
end
def check_for_primes(nums)
(2...nums).each do |i|
if nums%i == 0
nums = false
break
end
end
if nums != false
count_primes(nums)
end
end
create_numbers
Try this:
START = 1
STEP = 2
class Integer
def prime?
return if self < 2
(2...self).each do |i|
return if self % i == 0
end
true
end
end
def create_numbers
num = START
while (num + STEP) < 100
num += STEP
primes << num if num.prime?
end
end
def primes
#primes ||= []
end
create_numbers
p primes
When you want to save the 'state' of something, put it in an instance variable (#var).
It'll be accessible outside of the current function's scope.
Also, try naming your variables differently. For instance, instead of 'nums', in the
create_numbers method, use 'num'. Since the variable is only referencing one number at a
time and not a list of numbers, naming it in the plural will confuse people (me included)...
Hope it helps,
-Luke
each time into count_primes you put a value into array (which should have a better name, btw). Unfortunately, each time it's a new variable called array and since no one outside the function can see that variable it's lost when the function ends. If you want to save the values you've already found you'll need to set some state outside your function.
I can think of 2 quick solutions. One would be to declare your storage at the top of create_numbers and pass it into both functions.
def count_primes(num, arr)
def check_for_primes(nums, arr)
The other would be to set a variable outside all the functions, $array, for example to hold the values.
$array = []
...
$array << num
Since the scope of $array is global (i.e. all functions have access to it) you have access to it from anywhere in the file and can just add things to it in count primes. Note that using globals in this way is generally considered bad style and a more elegant solution would pass parameters and use return values.

What is the fastest way of summing set bits in Ruby's NArray?

I used NArray to implement a bit array, but I am not quite satisfied with the speed of the bits_on method. Currently I have:
# Method that returns the number of bits set "on" in a bit array.
def bits_on
bits_on = 0
self.byte_array.each do |byte|
bits_on += #count_array[byte]
end
bits_on
end
byte_array is an NArray.byte() type, and #count_array is build like this:
# Method that returns an array where the element index value is
# the number of bits set for that index value.
def init_count_array
count_array = []
(0 ... (2 ** BitsInChar)).each do |i|
count_array << bits_in_char(i)
end
count_array
end
Ideas?
Cheers,
Martin
I am not sure I understand the background correctly, a possible solution is:
def bits_on
NArray.to_na(#count_array)[self.byte_array].sum
end
Sorry, the above is wrong, the next will work:
def bits_on
index = NArray.int(*self.byte_array.shape)
index[] = self.byte_array
NArray.to_na(#count_array)[index].sum
end

Resources