Turning values into percent of the sum of all the values - ruby

Lets say I have the code below. Is there any code that will turn the values into percents of the sum of all the values. For example, since the sum of all the points is 200, Harrison will be 0.4 (40%)
kids_points {
Harrison: 80,
Jack: 70,
Justin: 30,
Max: 20,
}

Here is a way :
kids_points = {
Harrison: 80,
Jack: 70,
Justin: 30,
Max: 20,
}
def calculate_average(hash = arg.dup)
sum_of_values = calculate_sum(hash.values)
hash.each { |k,v| hash[k] = (100 * v) / sum_of_values }
end
def calculate_sum(enum)
enum.reduce(:+).to_f
end
calculate_average(kids_points)
# => {:Harrison=>40.0, :Jack=>35.0, :Justin=>15.0, :Max=>10.0}
Read this Enumerable#reduce and Hash#values method.

If you use active_support you can do this:
require 'active_support/all'
sum = hash.values.sum
hash.each { |k,v| hash[k] = (100.0 * v) / sum }
Note that 100.0 is necessary when percentages are not integer (most cases) or they won't sum up 100. Example:
hash = {
Harrison: 60,
Jack: 60,
Justin: 50,
Max: 50
}
# With 100.0
# => {:Harrison=>27.272727272727273, :Jack=>27.272727272727273, :Justin=>22.727272727272727, :Max=>22.727272727272727}
# With 100
# => {:Harrison=>27, :Jack=>27, :Justin=>22, :Max=>22}

Related

the result from my custom each method is producing a nil in ruby

def custom_each(array)
index = 0
while index < array.length
yield array[index]
index += 1
end
end
ages = [12, 45, 67, 89]
p custom_each(ages) { |num| num * 2 }
Any ideas? im a total beginner?
im doing this course https://www.rubyguides.com/2018/10/ruby-map-method/
Your custom_each method works just fine. All you have to do is move p inside the block:
# built-in each
ages.each { |num| p num * 2 }
# your custom_each
custom_each(ages) { |num| p num * 2 }
Output:
24
90
134
178
Note that the original Array#each doesn't return an altered array either. It returns an enumerator or the original array:
[12, 45, 67, 89].each
#=> #<Enumerator: [12, 45, 67, 89]:each>
[12, 45, 67, 89].each { |num| num * 2 }
#=> [12, 45, 67, 89]
By default last executed expression in the function will returned. The result of the block execution yield array[index] haven't saved and returned from the function custom_each. That's why it returns nil always
Try the below:
def custom_each(array)
index = 0
result = []
while index < array.length
result << yield(array[index]) # Saves the result of block execution in an array
index += 1
end
result # Returns the result
end
ages = [12, 45, 67, 89]
p custom_each(ages) { |num| num * 2 }
Output:
[24, 90, 134, 178]

How to return "invalid" instead a sum of an array when input is a string or a float

I want to return an array of sums of multiples of 3 and 5 from 0 to n. And I want to return "invalid" when the input is a string, a float or < 0
def is_multiple_of_3_or_5(n)
if n.class == Integer && n > 0
n % 3 == 0 || n % 5 == 0 ? true : false
else
puts "invalid"
end
end
def sum_of_3_and_5_multiples(n)
if n.class == Integer
i = 0
array_of_multiples_of_3_and_5 = Array.new
while i < n
array_of_multiples_of_3_and_5 << i if is_multiple_of_3_or_5(i) == true
i += 1
end
array_of_multiples_of_3_and_5.inject(0, :+)
end
end
sum_of_3_and_5_multiples(-1)
To get the sums of multiples of 3 and 5 I got this but when I try with -1 that return me 0 instead "invalid", with"string"` that return me an error.
You havent' put any code in your sum_of_3_and_5_multiples method to handle what happens if is_multiple_of_3_or_5 is invalid (or to put it another way, a string). You also don't need to puts 'invalid', as this returns a value of null. Just 'invalid' will do:
def is_multiple_of_3_or_5(n)
if n.class == Integer && n > 0
n % 3 == 0 || n % 5 == 0 ? true : false
else
"invalid"
end
end
def sum_of_3_and_5_multiples(n)
if n.class == Integer
i = 0
array_of_multiples_of_3_and_5 = Array.new
while i < n
return "invalid" if is_multiple_of_3_or_5(i).is_a?(String)
array_of_multiples_of_3_and_5 << i if is_multiple_of_3_or_5(i) == true
i += 1
end
array_of_multiples_of_3_and_5.inject(0, :+)
end
end
sum_of_3_and_5_multiples(-1)
=> "invalid"
One could do that as follows.
def sum_of_3_and_5_multiples(n)
case n
when Float, String, -Float::INFINITY...0
return 'invalid'
end
((0..n).step(3).to_a + (0..n).step(5).to_a).uniq
end
sum_of_3_and_5_multiples(11.5)
#=> "invalid"
sum_of_3_and_5_multiples("11")
#=> "invalid"
sum_of_3_and_5_multiples(-340)
#=> "invalid"
sum_of_3_and_5_multiples(15)
#=> [0, 3, 6, 9, 12, 15, 5, 10]
sum_of_3_and_5_multiples(87)
#=> [0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45,
# 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87,
# 5, 10, 20, 25, 35, 40, 50, 55, 65, 70, 80, 85]
Alternative verbose option, using a monkey patch to String class and a custom Class, stealing from Cary's answer (https://stackoverflow.com/a/59876202/5239030) but with a three dot Range for excluding the extreme value.
Patching the String class for using methods like this Numeric#integer? and Numeric#positive?. I'd suggest to use Kernel#raise in case of error.
module MyStringPatch
def integer?
false
end
def positive?
false
end
end
String.include MyStringPatch
Writing the custom class
class MyNumber
def initialize(n)
raise 'Invalid' unless n.integer? && n.positive?
#n = n
end
def sum_of_3_and_5_multiples
(((0...#n).step(3).to_a + (0...#n).step(5).to_a).uniq).sum
end
end
Finally using it
n = 32
my_number = MyNumber.new(n)
p my_number.sum_of_3_and_5_multiples
#=> 225
Or ...in initialize': Invalid (RuntimeError) in case of n = "32" or n = -32 or n = 32.0.
You can use something like:
return "invalid" unless n.is_a? Integer || n.positive?
Taking a look at: https://rubystyle.guide/ may help
I've found this ! that worked !
def is_multiple_of_3_or_5(n)
n % 3 == 0 || n % 5 == 0 || n == 0 ? true : false
end
def sum_of_3_and_5_multiples(n)
puts n.class
if n.class == Integer && n >= 0
i = 0
array_of_multiples_of_3_and_5 = Array.new
while i < n
array_of_multiples_of_3_and_5 << i if is_multiple_of_3_or_5(i) == true
i += 1
end
return array_of_multiples_of_3_and_5.inject(0, :+)
end
if n.class != Integer || n < 0
return "invalid"
end
end
thanks for your help that was helpful !

How do I make my class objects return an array instead of <Classobject:0x007fc94a0...]>?

!!!! I'm cleaning up my code and rethinking my question. I'll repost and edited version in a few minutes. Thanks for the responses!
Here's my code:
class Student
attr_accessor :scores, :first_name
def initialize(first_name, scores)
#first_name = first_name
#scores = scores
end
def average
#scores.inject {|sum, el| sum + el }.to_f / #scores.size
end
def letter_grade
case average
when (90..100)
"A"
when (80..89)
"B"
when (70..79)
"C"
when (60..69)
"D"
when (0..59)
"F"
end
end
end
me = Student.new("Alex", [100,100,100,0,100])
student_2 = Student.new('Hodor', [2,2,7,0,90])
student_3 = Student.new('Aria', [90,100,87,90,90])
student_4 = Student.new('Tyrion', [95,100,97,100,30])
student_5 = Student.new('Jaela', [100,100,100,100,100])
students = [me, student_2, student_3, student_4, student_5]
p students
Here's what I get back:
[#<Student:0x007f92a91e6070 #first_name="Alex", #scores=[100, 100, 100, 0, 100]>, #<Student:0x007f92a91e5ff8 #first_name="Hodor", #scores=[2, 2, 7, 0, 90]>, #<Student:0x007f92a91e5f80 #first_name="Aria", #scores=[90, 100, 87, 90, 90]>, #<Student:0x007f92a91e5f08 #first_name="Tyrion", #scores=[95, 100, 97, 100, 30]>, #<Student:0x007f92a91e5e90 #first_name="Jaela", #scores=[100, 100, 100, 100, 100]>]
I want something like [["Alex", [100, 100, 100, 0, 100], ["Hodor", [2..]..]]
The goal is to have these tests pass:
p linear_search(students, "Alex") == 0
p linear_search(students, "NOT A STUDENT") == -1
So I actually need this to happen within the Student class, I think.
I'm not sure what's the purpose of the exercise, but to get from your actual output to your expected output, you just have to go over your elements, and build an array out of each one (use map):
students.map { |x| [x.first_name, x.scores] }
# => [["Alex", [100, 100, 100, 0, 100]], ["Hodor", [2, 2, 7, 0, 90]], ["Aria", [90, 100, 87, 90, 90]], ["Tyrion", [95, 100, 97, 100, 30]], ["Jaela", [100, 100, 100, 100, 100]]]
If you try to output an instance of Student, ruby calls to_s() on the Student instance. If your class does not provide a to_s() method, then the inherited to_s() method(in class Object) is called, which provides the string you see. If you redefine Object#to_s, you can prove that:
#Your code here
class Object
def to_s
'hello from Object#to_s'
end
end
p students
--output:--
[hello from Object#to_s,
hello from Object#to_s,
hello from Object#to_s,
hello from Object#to_s,
hello from Object#to_s]
If you override the to_s() method inside Student, then ruby will call it and use its return value whenever you try to output a Student object:
require 'pp'
class Student
attr_accessor :scores, :first_name
...
def to_s
"#{first_name} #{scores.inspect}"
end
end
students = [
Student.new("Alex", [100,100,100,0,100]),
Student.new('Hodor', [2,2,7,0,90]),
Student.new('Aria', [90,100,87,90,90]),
Student.new('Tyrion', [95,100,97,100,30]),
Student.new('Jaela', [100,100,100,100,100]),
]
pp students
--output:--
[Alex [100, 100, 100, 0, 100],
Hodor [2, 2, 7, 0, 90],
Aria [90, 100, 87, 90, 90],
Tyrion [95, 100, 97, 100, 30],
Jaela [100, 100, 100, 100, 100]]
In the code fragment scores.inspect the inspect() method is what p uses, i.e. p scores is equivalent to print scores.inspect + "\n". But you can't write:
"some string #{p.scores}"
because string interpolation uses the return value of p.scores, and p, like puts, always returns nil.

Select an array item using a number range

I have four sizes, each are in a hash by their square feet:
# The sizes. Sizes are in sq ft (min, max)
size_hash = {
'Large' => [70,139],
'Medium' => [15,69],
'Small' => [1,14]
}
If I'm given the number 40, how can I return Medium as the size from the array?
Do I need to do something like this?:
# The sizes. Sizes are in sq ft (min, max)
size_hash = {
[70..139] => 'Large',
#... etc
}
You could use a proc:
size_store = proc{ |n|
case n
when 70..139
'Large'
when 15..69
'Medium'
when 1..14
'Small'
end
}
# USAGE: size_store[40]
size_hash.find{|_, (min, max)| (min..max) === 40}[0]
# => "Medium"
But I think it is a better idea to store ranges instead of min and max in the first place.
size_hash = {
'Large' => 70..139,
'Medium' => 15..69,
'Small' => 1..14,
}
size_hash.find{|_, r| r === 40}[0]
# => "Medium"
Yet another solution taking care of edge cases ...
#size_hash = {
'Large' => 70..139,
'Medium' => 15..69,
'Small' => 1..14,
}
some_value = #size_hash["Small"]
#min = some_value.first
#max = some_value.last
#size_hash.each_pair do |k, v|
#min = [#min, v.first].min
#max = [#max, v.last].max
end
puts "size_hash goes from #{#min} to #{#max}"
# Given a number, return the name of the range which it belongs to.
def whichSize(p_number)
#size_hash.each_pair do |k, v|
return k if v.include?(p_number)
end
return "below minimum" if p_number < #min
"above maximum" if p_number > #max
end
# test
[-10, 0, 1, 10, 14, 15, 20, 69, 70, 80, 139, 140, 1000].each do |e|
puts "size of #{sprintf("%4s", e)} is #{whichSize(e)}"
end
$ ruby -w t.rb
size_hash goes from 1 to 139
size of -10 is below minimum
size of 0 is below minimum
size of 1 is Small
size of 10 is Small
size of 14 is Small
size of 15 is Medium
size of 20 is Medium
size of 69 is Medium
size of 70 is Large
size of 80 is Large
size of 139 is Large
size of 140 is above maximum
size of 1000 is above maximum
If you stored ranges as keys, you could do this:
size_hash = Hash.new {|hash, key| hash.find {|range,_| range.cover?(key) }.last }.merge({
(1..14) => 'Small',
(15..69) => 'Medium',
(70..139) => 'Large'
})
This sets a default proc in the hash, so when you look up a value, like size_hash[9], the first range covering that value is returned. Bear in mind that this doesn't handle out-of-range errors.

Regular space subdivision

I am writing an application which subdivides an N-dimensional axis aligned bounding box into smaller N-dimensional bounding boxes, I need an algorithm which will do this.
For example:
in 1 dimension a "bounding box" is simply a length
e.g. { Min=0, Max=100 }
which would be subdivided into
{Min=0, Max=50} and {Min=50, Max=100}
in 2 dimensions a "bounding box" is a square
e.g. {Min=[0,0], Max=[100,100]}
would be divided into
{Min=[0,0], Max=[50,50]}
{Min=[0,50], Max=[50,100]}
{Min=[50,0], Max=[100,50]}
{Min=[50,50], Max=[100,100]}
And so on, all I need is a description of an algorithm for doing this, language doesn't particularly matter, since once I know how to do it I can translate it into the language of choice (C# in this case)
EDIT:: In response to questions in comments:
subdivisions must always be equal (as
in the examples)
boundaries are
floating points, so divisibility by two isn't a problem
Break it into two problems: iterating over the grid of "Min" points, and constructing a small box for a Min point.
For your second case, {[0,0], [100,100]}, deltaX=50 and deltaY=50. The grid is
[0, 0]
[0, 50]
[50, 0]
[50, 50]
and it is trivial to construct the second column from the first:
[ 0, 0] [ 50, 50]
[ 0, 50] [ 50, 100]
[50, 0] [100, 50]
[50, 50] [100, 100]
Here's a three-dimensional case {[0,0,0], [100,100,60]}, delta = [50, 50, 30]
[ 0, 0, 0] [ 50, 50, 30]
[ 0, 0, 30] [ 50, 50, 60]
[ 0, 50, 0] [ 50, 100, 30]
[ 0, 50, 30] [ 50, 100, 60]
[50, 0, 0] [100, 50, 30]
[50, 0, 30] [100, 50, 60]
[50, 50, 0] [100, 100, 30]
[50, 50, 30] [100, 100, 60]
A function that splits the box in all dimensions (in Python):
def halfboxes(box):
result = [[]]
for (a, b) in box:
result = [r + [(a, (a+b)/2)] for r in result] + \
[r + [((a+b)/2, b)] for r in result]
return result
h = halfboxes([(0,100), (20, 100)])
# Results in h =
# [[(0, 50), (20, 60)], [(50, 100), (20, 60)],
# [(0, 50), (60, 100)], [(50, 100), (60,100)]]
If this is's a good solution also depends your performance requirements. It makes a lot copies of arrays, which is not really efficient. But it might well be good enough for your use case.
Edit:
A more efficient version, that doesn't copy any arrays:
def halfboxes(box):
# total number of resulting arrays
resultscount = 2**len(box)
# allocate |resultscount| arrays
results = [[] for i in range(resultscount)]
spread = 1
for (a,b) in box:
low = (a, (a+b)/2)
high = ((a+b)/2, b)
for i in range(resultscount):
# "magic" to append the high/low parts to the correct array
if i % (spread*2) < spread:
results[i].append(low)
else:
results[i].append(high)
spread *= 2
return results
Here no arrays are copied and some calculations on the index are used to decide where the new boundaries should be added.
calculate the first box:
dimension n: Min[0, 0, 0, .., 0] -- Max[delta1/2, delta2/2, ..., deltan/2]
your big box will be subdivised to 2n small boxes -> calculate 2n transalations to apply to the 1st box (including a translation of [0, 0, 0, .., 0])
(of course the code below is not optimized-organized...)
using System;
using System.Collections.Generic;
namespace WindowsFormsApplication1
{
public class Class1
{
public static List<Box> GetSmallBoxes(Box bigBox)
{
int translationCoef;
List<Box> boxes = new List<Box>();
Box box;
for (int k = 0; k < Math.Pow(2, bigBox.Dimension); k++)
{
box = new Box(bigBox.Dimension);
for (int d = 0; d < bigBox.Dimension; d++)
{
translationCoef = ((int)(k / Math.Pow(2, bigBox.Dimension - d - 1)) % 2) == 0 ? 1 : 0;
box.Mins[d] = bigBox.Mins[d] + (bigBox.Deltas[d] / 2) * translationCoef;
box.Maxs[d] = bigBox.Mins[d] + (bigBox.Deltas[d] / 2) * (1 + translationCoef);
}
boxes.Add(box);
}
return boxes;
}
public static void Main()
{
Box bigBox = new Box(5);
bigBox.Mins = new int[] { 0, 10, 30, 20, 40 };
bigBox.Maxs = new int[] { 80, 50, 110, 40, 50 };
List<Box> smallBoxes = Class1.GetSmallBoxes(bigBox);
}
}
public class Box
{
public int Dimension;
public int[] Mins;
public int[] Maxs;
public Box(int dimension)
{
Dimension = dimension;
Mins = new int[dimension];
Maxs = new int[dimension];
}
public int[] Deltas
{
get
{
int[] deltas = new int[Dimension];
for (int i = 0; i < Dimension; i++)
{
deltas[i] = Maxs[i] - Mins[i];
}
return deltas;
}
}
public override string ToString()
{
string str;
str = "Min[";
foreach (int min in Mins)
{
str += min.ToString() + ", ";
}
str = str.Substring(0, str.Length - 2);
str += "] -- Max[";
foreach (int max in Maxs)
{
str += max.ToString() + ", ";
}
str = str.Substring(0, str.Length - 2);
return str;
}
}
}

Resources