Why don't the effects of my Ruby `for` loop persist? - arrays

I'm trying to put all unique combinations of a number's individual digits into an array. I'm passing in an integer and then using the permutation method. But to do so I believe I need to convert the number argument into a string. However when I iterate through the array, it is not joining the strings such that I can convert to an integer. Also I realize I should be using each to do the loop and not for but not sure how to do this in the block.
def number_shuffle(number)
combo = []
combo = number.to_s.split("").permutation.to_a
for i in combo
i.join.to_i
end
return combo
end
Is there something wrong with my loop? When I test one item in the array using combo[0].join.to_i I get an integer as output. But for some reason my loop is leaving these as strings.

In your example attempt, you failed to modify the contents of the array. I've replaced your for loop with a call to Array#map, which has a return value of a new Array containing the results of each iteration in order. Here is a verbose version of the method you want (for clarity).
def number_shuffle(number)
permutations = number.to_s.split('').permutation
numbers = permutations.map do |digits|
digits.join.to_i
end
return numbers
end
number_shuffle 123 # => [123, 132, 213, 231, 312, 321]
If you prefer a one-liner (replacing split with chars, per #timbetimbe below):
def number_shuffle(n)
n.to_s.chars.permutation.map {|x| x.join.to_i }
end

The reason why your for loop does not mutate combo is because it does nothing to it. The interpreter just evaluates i.join.to_i but does not edit the value in place.
Secondly, you do not need to use for, return, split('') or to_a on the return from permutation as they are not really idiomatic approaches. A more concise approach would be:
def number_shuffle(number)
number.to_s.chars.permutation.map { |combo| combo.join.to_i }
end
Reasoning:
favor each over for as it's preferred in the community.
use map from permutation as it includes Enumerable (same as Array) so you can just jump right into map
returns are implicit, embrace that :)
Linters help tremendously when getting started: Rubocop
This style guide is enforced by: bbatsov's Style Guide

Even faster and more efficient (to not have to copy anything in memory):
def number_shuffle(number)
number.to_s.split('').permutation.to_a.map do |combination|
combination.join.to_i
end
end

Related

Why can't I use the Match method on an array in ruby?

I need to iterate and parse 108 lines from a file and then sort them into 3 different hashes. (In one iterator.) (In Ruby)
I have the file loaded into the program and into the array I need to parse. When I try to make the iterator anyway I try to use the Regex Match command I get an error abut the unknown method. Is as simple that I can't use that method on a array?
lines = File.readlines('access_log')
lines.each.match(/^([:\d\.]+) .*\[.*\].*\"[A-Z]+ *(.+) HTTP/)
This and every other way I have tried to use the match method it hasn't worked.
This also doesn't anything for the hash, as I haven't done that yet.
/^([:\d\.]+) .*\[.*\].*\"[A-Z]+ *(.+) HTTP/.match(lines)
Have also tried this, but the error output appears that you cant run it on only the array. I beilive this is where I would need to tie the iterator in, but I'm stumped.
So, what's happening is that what readlines does is it slurps the entire text file.
So you have an array with the content of the textfile separated by a newline(and the newline is kept in every string in the array).
After that, you're doing lines.each, which brings out an enumerator. Then you're calling .match on the enumerator instead of the string itself
The proper way to do this would be
lines.each { |line| line.match(/^([:\d\.]+) .*\[.*\].*\"[A-Z]+ *(.+) HTTP/) }
However, the above actually won't do anything because all you're doing is iterating against each element and checking if it matches the REGEX.If you want it to actually do something, try...
matches = lines.map { |line| line.match(/^([:\d\.]+) .*\[.*\].*\"[A-Z]+ *(.+) HTTP/) }
Remember that the match method only works on strings. If match matches something, it returns an object of the class MatchData, else if it doesn't match anything-- nil.

How to best modify an array using .each

I am using Ruby, and I am relearning arrays and trying to better understand them. I know what they are but have never fully utilized them. I have an array, odds, and wanted to double every number in it. I came up with the below solution; however, I wanted to see if there was a more elegant/simple solution to my problem.
odds = [1,3,5,7,9]
odds.each do |x|
odds[odds.index(x)]=x*2
end
end result is odds = [2,6,10,14,18]
You can use the map! enumerator to modify every item in an array:
odds.map!{ |x| x*2}
If you really want to modify in place, and you really want to use each, I guess your approach is as good as any. It doesn't feel idiomatic, but it does meet your stated constraints.
Here are some more common approaches:
Array.map
Mapping the array with map won't modify the original array (it's not in-place), which is often a good thing, but it might not be what you're looking for:
odds.map { |x| x*2 }
Array.map!
If you really do want to modify the original array, you can use map! to map in-place:
odds.map! { |x| x*2 }
Array.each_index
You did ask specifically about each, so if you want to use an each and you want to modify the original array, each_index might be your best bet:
odds.each_index { |i| odds[i] *= 2 }

Ruby - Error accessing instance variables

I'm learning Ruby, and I'm having a problem while making a program.
I have a class "LineAnalyzer" that has 4 parameters (2 provided and 2 calculated). Both calculated params are: #high_wf_count (integer) and #high_wf_words (array).
Then, I have this one:
class Solution < LineAnalyzer
attr_reader :analyzers,
:highest_count_across_lines,
:highest_count_words_across_lines
def initialize
#analyzers = []
end
def analyze_file
File.foreach('test.txt') do |line|
#analyzers << LineAnalyzer.new(line.chomp,#analyzers.length+1)
end
end
def calculate_line_with_highest_frequency
#highest_count_words_across_lines = []
#highest_count_across_lines = #analyzers.max_by do
|a| a.instance_variable_get(:#highest_wf_count)
end .instance_variable_get(:#highest_wf_count)
#highest_count_words_across_lines << #analyzers.each do
|a| a.instance_variable_get(:#highest_wf_count) == #highest_count_across_lines
end .instance_variable_get(:#highest_wf_words)
end
end
The problem is that I cannot append the array #highest_wf_count to #highest_count_words_across_lines in the way I've done (it returns nil). But, I've previously taken the integer #highest_wf_count in the same way perfectly.
Can anyone tell me where's the problem?
Thanks in advance!
It seems that your problem is in this bit of code:
#highest_count_words_across_lines << #analyzers.each do
|a| a.instance_variable_get(:#highest_wf_count) == #highest_count_across_lines
end .instance_variable_get(:#highest_wf_words)
Preferably formatted as:
#highest_count_words_across_lines << #analyzers.each do |analyzer|
analyzer.instance_variable_get(:#highest_wf_count) == #highest_count_across_lines
end.instance_variable_get(:#highest_wf_words)
The problem here is that you are calling .instance_variable_get(:#highest_wf_words) on the result of the :each method.
A few lines above, you are doing something similar, where you call .instance_variable_get(:#highest_wf_count) on the result of the :max_by method, and it is working.
The difference between :max_by and :each is that :max_by returns a single analyzer, whereas :each returns the array of #analyzers over which it is iterating.
When you call :instance_variable_get(:#highest_wf_words) on that array, it's returning nil because an array will not have an instance variable named :#highest_wf_words
That is where your problem exists.
Sidenote:
It is generally not good practice to ever use :instance_variable_get. I would recommend adding to your analyzer class attr_reader :highest_wf_words, :highest_wf_count
Then, instead of calling analyzer.instance_variable_get(:#highest_wf_words), you can just call analyzer.highest_wf_words
There's a lot going on here and most of the code results from going against the grain when writing Ruby. Using instance_variable_get should be an absolute last resort. It's considered highly rude to just reach into an object and pull out a variable. It creates ugly and undesirable inter-dependencies. If that other object wanted to give you that value it would have a method to access it.
The way I see it what you're trying to do boils down to something like this:
def highest_frequency
#analyzers.map do |a|
a.highest_wf_count
end.sort.last
end
Let Analyzer implement highest_wf_count as a method, even if it's just an attr_reader. This gives you the flexibility to change how and when that value is computed. Maybe you don't need to do it when the object is initialized. Maybe it's done in another thread, or it's evaluated lazily.
Whenever possible try and structure your code as a series of straight-forward transformations. Try not to create convoluted, branching, ugly comparisons. Lean on Enumerable whenever possible, it's usually got a method that does exactly what you want, or two that in conjunction do the job perfectly.
This is way more complex than it needs to be (or should be).
Why does Solution subclass LineAnalyzer? And why are you using instance_variable_get? You should define getter methods using attr_reader on the LineAnalyzer class so you can call methods instead of using instance_variable_get, which is a brute force approach that should only be used as a last resort.
I think you should fix this before proceeding.
When you have instance methods created with attr_reader, calculating the max becomes very simple:
highest_count_across_lines = #analyzers.map(&:highest_wf_count).max
I think your error probably is caused by these lines:
#highest_count_words_across_lines << #analyzers.each do
|a| a.instance_variable_get(:#highest_wf_count) == #highest_count_across_lines
end .instance_variable_get(:#highest_wf_words)
I suggest simplifying this code, and the error will probably present itself to you. Did you really mean to append the value returned by each to #highest_count_words_across_lines? This will be an Array of analyzers. The Array class, of course, does not have a variable named :#highest_wf_words.
Again, I think you really need to simplify this code.

Why the array could not hold the keys of hash in the following perl script?

hash_test.pl
#a=("f","a","b");
$K{"f"}{"aa"}=1;
$K{"a"}{"aa"}=1;
$k{"b"}{"bb"}=1;
foreach(#a){
#c= sort keys %{$k{$_}};
}
print "#c\n";
foreach(#c) {...}
perl hash_test.pl
bb
I want to keep the keys of the hash into an array, so that I can use the array as an input for the following statements.
But it seemed that the assay #c just only hold the last element.
Could anyone tell me why or help me to improve the script?
You assign the array every time in the foreach, thus overwriting it every time. So you end up with only having the last thing assigned to it. If you move the print inside the foreach you'll see that they are all there.
To store those keys you need to add them to the array, not assign the array. I've corrected the typo $k to $K, and changed aa that goes with f to ff (expecting it to be a typo as well).
my #c;
foreach my $el (#a) {
push #c, sort keys %{$K{$el}};
}
print "#c\n";
This prints the line: ff aa bb. Every time through the loop all keys found in the hash for a particular array element are added to #c, each as a separate element. So #c will contain all bottom-level keys across the whole data structure.
However, there is more that I would like to suggest.
Always use strict; and use warnings; This is not pedantry but it directly helps. I never write code without them. The typo would be caught here, for example.
Use descriptive variable names. Specifically, single-letter variable names are just too easy to confuse, unless in very short loops or where it is crystal clear what they are. (For example, a typo like this couldn't really happen.) Most importantly, the code is going to be far nicer to work with. That generally results in better code.
Please use good indentation and spacing. It helps a lot, in many ways.
A useful core package for nested data structures is Data::Dumper, which can print the whole thing nicely formatted so we can see it. Try to add to the end of your code
use Data::Dumper;
print Dumper(\%K);
There are yet others that do the same or similar.
Here is another way to do what you ask.
my #lowest_keys = map { sort keys %{$K{$_}} } #a;
I call them lowest_keys to emphasize that these are the ones from the last hash in your data structure, the bottom of it. The map applies processing in the block { ... } to each element of #a in turn, returning a list with all these results. (If any one result itself is a list, with more elements than one, it gets merged into the overall output list. So this may create the output list with many more elements than the input.) This list can then be assigned to an array, as above, or passed on to another function that expects a list as input, or interated over.
The map is generally used to transform an array into another, by doing to each element what is in { ... } block. Its close cousin grep is used to filter, so passing through only the elements of the input list for which the condition in { ... } evaluates to true, forming the output list. For example, filter out undefined array elements: my #good = grep { defined } #all
Variable names are case sensitive, so %K... and %k... are not the same.
Always use
use strict;
use warnings;
and declare your variables with my. That prevents you from making this kind of mistakes.

Perl: Reading from file and saving into an array

I had got a script in Perl and my task is to do some changes in it. This of course means to understand which part does exactly what. I am not familiar with Perl language but I tried to read through some tutorials, but still some things are confusing me. And I got stuck in following part:
while (<KOEFICIENTYfile>) {
#_=(split(/\s+/, $_));
push(#ZAID,shift(#_));
$KOEFICIENTY{$ZAID[-1]}=[#_];
}
As I understands this part then it:
Reads line from filehandle KOEFICIENTYfile
Separates them by spaces (one or more)
Loads first item from this separated array into array ZAID (and in the process, removes it from #_)
??? Adds a rest of a separated array into array KOEFICIENTY? I am confused by curly brackets part and by square brackets after equals sign.
I think that I understood the meaning of #, $, #_ or negative indexing but this is beyond me. Can you please advice me on meaning of this?
[-1] indexing is just a shortcut way to say "last element of the array".
KOEFICIENTY is actually a hash (you can tell this because it is using curly braces, instead of square ones, around the index), so you're putting the rest of the array #_ into a hash called KOEFICIENTY with a key of the last element of the array.
If you include:
use Data::Dumper
at the top of the script and do
print Dumper(%KOEFICIENTY)
it will nicely format the output for you, which may help
The original coder was trying to be too clever using the negative offset. It would have been much more obvious if the code had been written with a simple temporary variable thus:
while (<KOEFICIENTYfile>) {
#_ = (split(/\s+/, $_));
my $key = shift(#_);
push(#ZAID, $key);
$KOEFICIENTY{$key} = [#_];
}
The braces on $KOEFICIENTY show that this is a "hash" of key/value pairs named %KOEFICIENTY, and not a normal array.
If you don't actually need to preserve the sort order of the keys you could just use keys %KOEFICIENTY to retrieve them instead of storing them in #ZAID.
#zaid is a list, into which the first part of the split is added.
%KOEFICIENTY is a hash, in which a reference to the rest of the split is stored as a list reference under the key of the first part.
So if the line is a b c, #zaid will get a and %KOEFICIENTY{'a'} will hold a reference to a list containing b and c.

Resources