Counting and computing the average length of words in ruby - arrays

I'm trying to debug a program in ruby that is meant to compute and print the average length of words in an array.
words = ['Four', 'score', 'and', 'seven', 'years', 'ago', 'our', 'fathers', 'brought', 'forth', 'on', 'this', 'continent', 'a', 'new', 'nation', 'conceived', 'in', 'Liberty', 'and', 'dedicated', 'to', 'the', 'proposition', 'that', 'all', 'men', 'are', 'created', 'equal']
word_lengths = Array.new
words.each do |word|
word_lengths << word_to.s
end
sum = 0
word_lengths.each do |word_length|
sum += word_length
end
average = sum.to_s/length.size
puts "The average is " + average.to_s
Obviously, the code is not working. When I run the program, I receive an error message that says the string '+' can't be coerced into fixnum (typeerror).
What do I do no make the code compute the average length of the strings in the array?

Try:
words.join.length.to_f / words.length
Explanation:
This takes advantage of chaining methods together. First, words.join gives a string of all the characters from the array:
'Fourscoreandsevenyearsagoourfathersbroughtforthonthiscontinentanewnationconcei
vedinLibertyanddedicatedtothepropositionthatallmenarecreatedequal'
We then we apply length.to_f giving the length as a float (using a float ensures an accurate final result):
143.0
We then divide the above using / words.length:
4.766666666666667

Try this.
words = ['Four', 'score', 'and', 'seven', 'years', 'ago', 'our', 'fathers',
'brought', 'forth', 'on', 'this', 'continent', 'a', 'new', 'nation',
'conceived', 'in', 'Liberty', 'and', 'dedicated', 'to', 'the', 'proposition',
'that', 'all', 'men', 'are', 'created', 'equal']
sum = 0
words.each do |word|
sum += word.length
end
average = sum.to_i/words.size
puts "The average is " + average.to_s
You don't have to have a separate word_lengths variable to keep all the sizes of words in words array. Without looping through the word_lengths array you can merge both loops into one loop as I have given in the post.
The way you're getting the length of the word is wrong. Use word.length. See here.

Related

Ruby - return boolean for matching one or both elements in an array element

I have an array that consists of strings with text separated by commas. I need to return a boolean that indicates if it is empty or if one or both of two other strings is the only value contained in the array element.
text1 = John
text2 = Doe
array1['element'] = 'John, Doe' #true
array2['element'] = 'Bob, Buck' #false
array3['element'] = 'John, Buck' #false
array4['element'] = 'John' #true
array5['element'] = 'John, John' #true
array6['element'] = '' #true
I can match one at a time or an empty element, but I'm not sure how to handle making sure only my matches are included and not other text.
foo = 'John,Doe,Buck'
if foo['John']
foo <= 'Set to Repeat'
elsif foo['Doe']
foo <= 'Set to Repeat'
elsif foo['John,Doe']
foo <= 'Set to Repeat'
elsif foo['']
foo <= 'Set to Repeat'
else foo
end
Using this code I get a match, but I need to reject it because of the presence of 'Buck'.
The question is a bit confusing because array1['element'] is invalid if array is an array. The method Array#[] must take one integer, two integers or a range as its argument(s). Giving it an argument that is a string will raise an exception (TypeError (no implicit conversion of String into Integer).
Suppose:
text = ['John', 'Doe']
arr = ['John, Doe', 'Bob, Buck', 'John , Buck', 'John', 'John, John', '']
and you wish to know which elements of arr (strings) contain only the words in the array text. You can do that as follows.
arr.map { |s| s.split(/ *, +/).all? { |ss| text.include?(ss) } }
#=> [true, false, false, true, true, true]
If, for example, s = 'Bob, Buck', then
s.split(/, +/)
#=> ["Bob", "Buck"]
Similarly,
'Bob'.split(/, +/)
#=> ["Bob"]
''.split(/, +/)
#=> []
See String#split, Array#all? and Array#include?. The regular expression / *, +/ reads, "match zero or more (*) spaces followed by a comma followed by one or more (+) spaces". (If 'John,Doe' is to be permitted use / *, */.)
Alternatively, one could write
arr.map { |s| s.split(',').all? { |ss| text.include?(ss.strip) } }
#=> [true, false, false, true, true, true]
Here
'John , Buck'.split(',')
#=> ["John ", " Buck"]
but then
"John ".strip
#=> "John"
" Buck".strip
#=> "Buck"
See String#strip.
You may wonder why I used Array#all? considering that all?'s receiver is an array containing only two elements (e.g., a = ['John', 'Doe']. It is simply because it is easier to regard a as an array of arbitrary size than to have one statement that requires text to include a[0] and another that requires text to include a[1].
Lastly, another variant would be to use String#scan:
arr.map { |s| s.scan(/[, ]+/).all? { |ss| text.include?(ss) } }
#=> [true, false, false, true, true, true]
scan takes an argument that is a regular expression that reads, "match one or more (+) characters, each of which is a character other than (^) a comma or a space". The brackets denote a character class, meaning that a character must match any character in the class. The ^ at the beginning of the class definition means "other than the characters that follow".

How to compress a string in-place

I have been working on this problem on leetcode https://leetcode.com/problems/string-compression/
Given an array of characters, compress it in-place.
The length after compression must always be smaller than or equal to the original array.
Every element of the array should be a character (not int) of length 1.
After you are done modifying the input array in-place, return the new length of the array.
I almost have a solution, but I can't seem to count the last character in the string and I also am not sure how to make it so if there is only an amount of one of a character that I do not show 1 in the array.
I feel like I'm pretty close and I'd like to try and keep the solution that I have without altering it too much if possible.
This is what I have so far. chars is a list of characters
def compress(chars):
char = 0
curr = 0
count = 0
while curr < len(chars):
if chars[char] == chars[curr]:
count += 1
else:
# if count == 1:
# break
# else:
chars[char-1] = count
char = curr
count = 0
curr += 1
chars[char-1] += 1
return chars
print(compress(["a", "a", "b", "b", "c", "c", "c"]))
I wasn't quite able to format your code to get the answer you were seeking. Based on your answer, I was able to put together code and explanation that could help you:
def compress(chars):
count = 1
current_position = 0
# if it's a single character, just return a
# a basic array with count
if len(chars) == 1:
chars.append("1")
return chars
# loop till the 2nd last character is analyzed
while current_position < len(chars) - 1:
# assume that we haven't reached the 2nd last character
# if next character is the same as the current one, delete
# the current one and increase our count
while current_position < len(chars) - 1 and \
chars[current_position] == chars[current_position + 1]:
del chars[current_position]
count += 1
# if next character isn't the same, time to add the count to
# the list. Split the number into
# character list (e.g. 12 will become ["1", "2"]
# insert those numbers behind the character and increment position
for x in str(count):
chars.insert(current_position + 1, str(x))
current_position += 1
# great, on to the next character
current_position += 1
# if we are now at the last character, it's a lonely character
# give it a counter of 1 and exit the looping
if current_position == len(chars) - 1:
chars.append("1")
break
count = 1
return chars
mylist = ["a","b","b","b","b","b","b","b","b","b","b","b","b"]
print(compress(mylist))
Results
mylist = ["a","b","b","b","b","b","b","b","b","b","b","b","b"]
['a', '1', 'b', '1', '2']
mylist = ["a","a","a","a","a","a","a","a","a","a","b","b","b","b","b","b","b","b","b","b","b","b"]
['a', '1', '0', 'b', '1', '2']
mylist = ["a"]
['a', '1']
mylist = ["a","b"]
['a', '1', 'b', '1']
mylist = ["a","a","b","b","c","c","c"]
['a', '2', 'b', '2', 'c', '3']

Ruby count integers in array

Is there a way I can count the number of integers in an array? I have an array whose members come from a-z and 0-9. I want to count the number of integers in said array. I tried:
myarray.count(/\d/)
...but the count method doesn't regex.
a = 'abcdefghijklmnopqrstuvwxyz'.split('')
a << [0,1,2,3,4,5,6,7,8,9]
t = a.sample(10)
p t.count(/\d/) # show me how many integers in here
The following should return the number of integers present within the array:
['a', 'b', 'c', 1, 2, 3].count { |e| e.is_a? Integer }
# => 3
Since #count can accept a block, we have it check if an element is an Integer, if so it will be counted towards our returned total.
Hope this helps!

Perl assign array to hash and vice-versa

I would like to get your help to figure out how to add a long array to a hash key value, how to assign the key value to a temporay array from where the data will be manipulated, than assign the array (or what I have left) back to the same hash key.
A sample of what I am looking for is below:
#!/usr/bin/perl
use strict;
use warnings FATAL => 'all';
My set of arrays that will contain only integers
my #Array1 = ['01', '02', '03', '04', '09', '10', '11', '12'];
my #Array2 = ['05', '06', '07', '08', '03', '04', '09'];
my #Array3 = ['09', '10', '11', '12', '02', '03', , '07', '08' ];
my #ArrayN = ['N', 'N', 'N', 'N'];
My hash with keys identified by integers (Is it possible?)
my %Hash;
Assigning arrays to key values;
%Hash{'1'} = #Array1 (or $Array1);
%Hash{'2'} = #Array2 (or $Array2);
%Hash{'3'} = #Array3 (or $Array3);
%Hash{'N'} = #ArrayN (or $ArrayN);
Assign a hash key value to a temporary array
my #TempArray = $Hash{'1'};
Some process with the temporary array including:
(Delete the first element/item of the temporary array, let's say the
numnber "01"');
(Check if the array has a especifc value, let's say numbers 03 and
09);
(Delete especific values from the array, let's say the
numnbers 03 and 09');
(Check how many elements/items exist in the array);
than assign the value back to the hash
%Hash{'1'} = #TempArray;
I have found many posts that are not helping so much, as they don't talk about long arrays.
By using square brackets for your lists, you've actually created an array reference, which is a scalar. Your assignments should thus be:
my $Array1 = ['01', '02', '03', '04', '09', '10', '11', '12'];
my $Array2 = ['05', '06', '07', '08', '03', '04', '09'];
etc.
Next, when assigning these references as hash keys, you must use the $ sigil since you're referring to the hash value (a scalar) and not the hash itself.
$Hash{'1'} = $Array1;
$Hash{'2'} = $Array2;
Now if you want to assign these references to array variables, you must dereference them:
my #TempArray = #{ $Hash{'1'} };
As for your list of operations/checks on these arrays:
Delete the first item:
shift #TempArray;
Check if a certain value is an element of the array:
my $check = grep { $_ == $some_number } #TempArray;
($check will be the number of matches returned, and thus will evaluate to false if you get zero matches);
Delete an specific element (index unknown) from the array:
Every occurrence of a certain value?
#TempArray = grep { $_ != $some_number } #TempArray;
A specific element (not necessarily first or last)? Need to know the offset.
splice #TempArray => $index;
Check the length of the array:
my $length = #TempArray;
(referring to an array in list context returns its length)
My set of arrays that will contain only integers
Actually, your arrays contain strings :-)
my #Array1 = ['01', '02', '03', '04', '09', '10', '11', '12'];
Here's your first serious error. Arrays are initialised from lists - so you need round brackets, (...), not square brackets, [...].
You wanted this:
my #Array1 = ('01', '02', '03', '04', '09', '10', '11', '12');
Alternatively, you could use your original square brackets. But they give you an array reference. A reference is a scalar value, so you store it in a scalar variable.
my $Array1_ref = ['01', '02', '03', '04', '09', '10', '11', '12'];
My hash with keys identified by integers (Is it possible?)
Well, the keys of a hash are always strings. But that's ok. Perl will seamlessly convert integers to strings. And it's not necessary here because you're actually using strings ('1'), not integers (1).
%Hash{'1'} = #Array1;
A couple of errors here. The values in a hash are scalars. So you access them using a scalar sigil ($, not %). And, of course, an array isn't a scalar so you can't store it in a hash value. You can, however, store an array reference in a hash value (as references are scalar values.
$Hash{'1'} = \#Array1; # use \ to get a reference
Alternatively, if you stuck with the [...] version and stored your array reference in $Array_ref, then you can do:
$Hash{'1'} = $Array_ref;
Assign a hash key value to a temporary array
The value in your hash is a reference to your array. So you need to dereference it before storing it in an array variable.
#Temp_Array = #{ $Hash{'1'} };
Delete the first element/item of the temporary array, let's say the numnber "01"'
shift #Temp_Array;
Check if the array has a especifc value, let's say numbers 03 and 09
if (grep { $_ eq '03' } #Temp_Array) {
say "03 is in the array";
}
Delete especific values from the array, let's say the numnbers 03 and 09
#Temp_Array = grep { $_ ne '03' and $_ ne '09' } #Temp_Array;
Check how many elements/items exist in the array
Simply evaluate the array in a scalar expression.
$num_of_elements = #Temp_Array;
then assign the value back to the hash
Once again, you need to take a reference to the array.
$Hash{'1'} = \#Temp_Array
It's worth pointing out that you don't need to copy the array to a temporary array variable in order to manipulate it. Anywhere that I have used #Temp_Array in the examples above, you can replace it with #{ $Hash{'1'} } and you will be manipulating the array inside the hash directly.
shift #{ $Hash{'1'} }; # for example
I have found many posts that are not helping so much, as they don't talk about long arrays.
That's probably because long arrays and short arrays (and middle-sized arrays) are all handled in exactly the same way in Perl.

mongo: converting a row oriented collection, to a column oriented collections

Suppose I have:
a large collection A, containing a large number of documents each made of the same row of large number of fields (eg. 10^8 rows x 300 fields)
we want to convert it into 300 collections each containing only one large array of values (300 arrays x 10^8 values)
This is possible since all rows have the same structure thus all resulting arrays have the same of elements. We may see that as a large (10^8 x 300) matrix.
I have tried an aggregation using the $concatArrays but it failed due to the 16Mo limit of aggregations.
This my try considering one of the fields 'F' of the collection 'A'. Since the number of documents is too large, I decided to split the process into 500000 steps. I convert this part of the collection into one single collection 'F', with only one document.
outname = 'F';
step = 500000
count = db.A.count()
out = db[outname];
out.drop();
out.insert({F:[]});
op1 = {$arrayElemAt: [ '$x.F', 0 ]};
op2 = {$concatArrays: ['$F', op1]};
start = 0
for(i = 0; start < count; i++)
{
tmp = 'tmp';
db[tmp].drop();
skip = start;
print(i, skip, start);
db.Object.aggregate(
[
{$project: {'F': 1} },
{$skip: skip},
{$limit: step},
{$group: {'_id': '', 'F': {$push: '$F' } } },
{$out: tmp}
],
{allowDiskUse: true}
);
join = {$lookup: {'from': tmp, 'localField': outname + '._id', 'foreignField': tmp + '._id', 'as': 'x'} };
out.aggregate( [ join, {'$project': {'F': op3} }, {'$out': outname} ], {allowDiskUse: true});
start += step;
}
Of course the mechanism based on $concatArrays is not scalable.
How would you improve this? (I mean efficiently since those arrays are really huge)
Thanks for suggestions
Christian

Resources