Match/compare a value with members of an array - ruby

I have an array containing four ranges:
[0..25, 26..50, 51..75, 76..100]
How can I match/compare an integer with this array? For example:
28 # => 26..50
89 # => 76..100
What is the best way to do this?

[0..25, 26..50, 51..75, 76..100].find{|r| r.include?(28)} # => 26..50

As #WandMaker said, there are range methods that can help you here
http://ruby-doc.org/core-2.2.0/Range.html#method-i-cover-3F
If I understand your question correctly, you want to check if an int is contained within your ranges?
my_array.each do |range|
if range.cover?(my_integer)
return true
end
end
return false

Regarding your comment:
I matched the number with a hash [:range => :score]. So, I have 4 ranges, scoring "bad" "so so" "ok" and "super". If my variable fits a range, it returns the score
You can use a case statement:
def score(number)
case number
when 0..25 then :bad
when 26..50 then :so_so
when 51..75 then :ok
when 76..100 then :super
end
end
score(28) #=> :so_so
score(89) #=> :super

Or if you just need to know if it matches any range:
p [0..25, 26..50, 51..75, 76..100].any?{|range| range.cover?(my_int) } #=> true
Note that cover? is faster than include?, and is safe to use when working with integers.

Related

Optimize print output where i use check on zero. Ruby

Currently, I'm having print like this
print ((stamp_amount[0], 'first mark') unless stamp_amount[0].zero?), (', ' if !stamp_amount[0].zero? && !stamp_amount[1].zero?),
((stamp_amount[1], 'second mark') unless stamp_amount[1].zero?)
stamp_amount is an array with 2 integer values
Let's say in the current situation stamp_amount[0] = 10 and stamp_amount[1] = 3
Output preview:
10 first mark, 3 second mark
So if stamp_amount[0] = 0 the 10 first mark, part won't be show. Same if stamp_amount[1] = 0 the , 3 second mark part won't be shown
For me, it seems a little bit incorrect in terms of theory. Could you please suggest me the more correct or less painful print of this? :)
Cheers!
Your code is trying to join a sequence of up to two elements with a separator. The joining is a solved problem, see Array#join.
The problem can be then reduced to "how can I produce the correct sequence, given my stamp_amount input". Now this can be done in a thousand ways. Here's one:
def my_print(stamp_amount)
ary = [
!stamp_amount[0].zero? && stamp_amount[0],
!stamp_amount[1].zero? && stamp_amount[1],
].select{|elem| elem }
ary.join(', ')
end
my_print([10, 3]) # => "10, 3"
my_print([0, 3]) # => "3"
my_print([10, 0]) # => "10"
my_print([0, 0]) # => ""
Here's another
ary = []
ary << stamp_amount[0] unless stamp_amount[0].zero?
ary << stamp_amount[1] unless stamp_amount[1].zero?
ary.join(', ')
Here's yet another. This version can handle stamp_amount of any length.
ary = stamp_amount.reject(&:zero?)
ary.join(', ')
I'd go with the third, but the second one may be the easiest to understand for a beginner.
Use the select, as an alternative to reject (shown in part 3 of the answer by Sergio Tulentsev). It is just asa readable, and depending on the context and on the future changes to the code, you may prefer one versus the other.
puts stamp_amount.select{ |a| !a.zero? }.join(", ")
A few examples of inputs and outputs are:
stamp_amount output
--------------------------------------------------------------------------
10, 3 10, 3
10, 0 10
0, 3 3
0, 0 (prints an empty line, because the selected array is empty)
You're calculating zero? on index points more often than is needed, but the first thing I would look at refactoring here is the readability of the code. It might be nicer to calculate the message to print outside of the print method and explain what is happening with variable names.
# rubocop is going to complain about variable assignment like this
first_amount, second_amount = *stamp_amount
We can actually use the reason rubocop prefers the .zero? over == 0 or .empty? method to guide our development. zero? is in essence just empty? but it communicates the meaning of what you are attempting to do in a better manner. I would use this reasoning when assigning strings to variables that explain what they are doing.
some_name_that_explains_what_this_is_0 = "#{first_amount} piecu centu marka"
some_name_that_explains_what_this_is_1 = "#{second_amount} tris centu marka"
Your current code is confusing as you have the possibility of printing a string like "10 tris centu marka" which does not make lexical sense and probably not what you are after considering tis evaluates to 'second mark', which would pose an issue if the first value is zero. We also could reject zero integers before we start converting them to strings.
array = [1, 0].reject(&:zero?)
Now we can take the array and do something like:
string = []
array.each_with_index { |e, i| string << "#{e} #{Ordinalize.new(i).ordinalize} mark" }
message = string.join(', ')
print(message)
# ord class
class Ordinalize
def initialize(value)
#value = value
end
def ordinalize
mapping[#value]
end
def mapping
# acounting for zero index
['first', 'second']
end
end
where we are calculating the ordinalization and letting our new class handle the sentence structure for us.
Outputs:
[1, 0] => "1 first mark"
[0, 1] => "1 first mark"
[1, 2] => "1 first mark, 2 second mark"

Ruby array contains types?

I need a function to check if an array contains other arrays, or more generally if an array contains a certain class. My naive first approach is:
found=false
[1,"a",[],:asdf].each { |x| found=(x.is_a? Array) ? true : found }
puts found
Any way to optimize this?
In newer Rubies (2.5+) you don't need to pass a block to any?. That links goes to the latest docs, which has a similar example checking for integers.
Therefore, you can simply use:
[1, "a", [], :asdf].any?(Array)
# => true
[1, "a", :asdf].any?(Array)
# => false
That seems the simplest syntax and most sensible approach here if your Ruby version allows it :)
You can try #any?
For example:
[1,"a",[],:asdf].any? { |el| el.is_a? Array }
#=> true
Almost always when you find yourself using each, you are missing some higher-level abstraction.
In this case, that abstraction is the method Array#any?, which will return true if any element of the array satisfies the predicate:
[1, "a", [], :asdf].any?(Array)
this approach iterate over all elements even if you found an array.
in your test case it will iterate over :asdf,
and you don't have to check this.
I think you need to break from loop if you found the certain type(Array in our Example).
found = false
[1,"a",[],:asdf].each do |x|
if x.is_a? Array
found = true
break
end
end
puts found
Also you can use any instead of each
found = [1,"a",[],:asdf].any? { |x| x.is_a? Array }
puts found # true

Check whether string match or not

I have a string and an array with some strings.
as below
hostname = TETDC3DBE01
Array = ['WEB','APP','STR','DBE']
I want to find whether that hostname match with any of the array element or not?
When I'm trying with below code getting output
no
no
no
no
Here is loop repeating each and every element on array. I want check that hostname with single check on array the produce the output either yes or no only.
Array.each do |x|
if hostname.match(x)
puts "yes"
else
puts "no"
end
end
Given this fixed Ruby code:
hostname = 'TETDC3DBE01'
array = ['WEB','APP','STR','DBE']
Where if you want to find all elements in array that match as a substring of hostname your code should work. The more minimal matching system is probably:
array.select { |x| hostname.match(x) }
# => ["DBE"]
Using a tool like puts to produce output isn't always very useful because that "yes" or "no" text can't be acted upon by more code. Try and think of Ruby programs as a chain of transformations, where this selects all matches, and later you can print them, like this:
puts array.select { |x| hostname.match(x) }.join(',')
# => DBE
Check out Array#any? method.
It passes each element of the collection to the given block. The method returns true if the block ever returns a value other than false or nil. If the block is not given, Ruby adds an implicit block of { |obj| obj } that will cause any? to return true if at least one of the collection members is not false or nil.
If instead a pattern is supplied, the method returns whether pattern === element for any collection member.
In your case:
hostname = 'TETDC3DBE01'
['WEB','APP','STR','DBE'].any? do |x|
hostname.match(x)
end
or even if you actually mean equal by match:
hostname = 'TETDC3DBE01'
['WEB','APP','STR','DBE'].any?(hostname)
Lets take your code to fix it.
hostname = "TETDC3DBE01"
arr = ['WEB','APP','STR','DBE']
arr.each do |x|
if hostname.match?(x)
puts "yes"
else
puts "no"
end
end
match gives array of result and
match? gives you true or false value
I wouldn't use regexp in this case. A simple String#include? is probably faster. Furthermore any? will return true if any of the elements in the array leads is matching.
hostname = 'TETDC3DBE01'
array = ['WEB','APP','STR','DBE']
array.any? { |x| hostname.include?(x) }
#=> true
Regular expression made real easy:
hostname = "TETDC3DBE01"
array = ['WEB','APP','STR','DBE']
re = Regexp.union(array)
hostname.match?(re) # => true

Converting to_i doesn't work (Ruby)

I'm pretty new to programming and i'm doing fundamentals on codewars.com and I'm having some trouble with this one. The objective is to take a group of integers, reverse them, and put them into an array. Here's my code. (I made the tf function to see what was going on in the code.)
def digitize(n)
answer = n.to_s.split(//).reverse!
def tf(it)
str_test = it.is_a? String
int_test = it.is_a? Integer
puts "String: #{str_test}"
puts "Integer: #{int_test}"
end
Array(answer)
unless answer.is_a? Integer
for item in answer
item.to_i
puts item
tf(item)
end
end
return answer
end
Sample test:
Test.assert_equals(digitize(35231),[1,3,2,5,3])
When tested, it returns:
1
String: true
Integer: false
3
String: true
Integer: false
2
String: true
Integer: false
5
String: true
Integer: false
3
String: true
Integer: false
Can one of you guys help me figure out where it goes wrong?
Assigning
item = item.to_i
Would fix the output in tf, but your returned answer would still be all strings. If you want to do this one by one like you're doing you would need to assign it back into the index of the array:
answer.each_with_index do |item, index|
answer[index] = item.to_i
end
Though, an even better way to do this would be with map (returns a new array) or map! (in-place):
# return this line (make it the last line in the method) or make sure
# to re-assign answer
answer.map(&:to_i)
# or do this one to use `answer` later on with all integers.
answer.map!(&:to_i)
(See this question about that &:to_i syntax).
It should also be noted (maybe), that Rubyists in general don't like for loops and prefer each loops.
Also, the line:
Array(answer)
doesn't modify the answer in place, and returns it cast to an array, so the line is doing nothing:
a = "1"
Array(a) # => ["1"]
a # => "1"
a = Array(a) # => ["1"]
a # => ["1"]
You also, don't even need to do this, since answer is already an array from where you split it (You could also have used chars instead of split(//)). The line unless answer.is_a?(Integer) will thusly never be true.
The last major thing, I see is that in newer versions of ruby, there's a built-in method to do all this, digits:
35231.digits # => [1, 3, 2, 5, 3]

iterate array combination method with .any method

Is there anyway to iterate through different combinations of arrays?
I'm writing a program that returns true if the largest number in an array can be the sum of any of the members of the array.
This is my code: (forgive me, i learned how to program 2 weeks ago for the first time)
def ArrayAdditionI(arr)
arr=arr.sort
largest=arr.pop
n=arr.length
for i in 1..n
if arr.combination(i).to_a.any? {|array| array.inject(:+)==largest}
return true
else
return false
end
end
end
i tested it for
a = [1,2,3,4]
and it returned false even though clearly, 3+1 = 4.
When I change the above code from arr.combination(i) to arr.combination(2) its returns true. So I'm guessing it has something to do with the combination method not being able to be looped. Any suggestions?
You have return false in the wrong place. As it is, false is returned is there is no combination of one element (i.e., one element other than the one you've removed after sorting) that sums (i.e., is equal to) largest. Rather you want to return false only if no combination of any size sums to largest. This is what you need:
def max_match?(arr)
arr=arr.sort
largest=arr.pop
n=arr.length
for i in 1..n
return true if arr.combination(i).any? {|array|
array.inject(:+)==largest}
end
false
end
arr = [1,4,7,3,5,2,13]
max_match?(arr) #=> true
arr = [1,3,4,7,59]
max_match?(arr) #=> false
A few notes:
lower case letters and optional underscores are used for names of methods. You can also put a question (?) or explanation (!) mark at the end. Here a question mark seems appropriate.
to_a is not needed. Enumerable methods (e.g., any?) can have Enumerators as receivers.
sorting is expensive and unnecessary. Instead, just get the max value and remove it from the array. (Aside: you didn't say what happens if the maximum value appears more than once. See the code below and the last sentence.)
you don't need n=arr.length. for i in 1..arr.length is enough, except...
for ... is rarely used. Rubyists tend to use each instead, or one of the many methods from the Enumerable (mix-in) module (all of which invoke each).
you don't need return false because Ruby returns the value of the last statement evaluated, which is false if the method does not return true earlier.
Here's a more Ruby-like way to do that:
def max_match?(arr)
mx = arr.max
whats_left = arr - [mx]
(1..whats_left.size).any? {|n| whats_left.combination(n).any? {|c|
c.reduce(:+) == mx }}
end
arr = [1,4,7,3,5,2,13]
max_match?(arr) #=> true
arr = [1,3,4,7,59]
max_match?(arr) #=> false
If you wish this to return true when arr contains more than one value equal to mx, insert the following after mx = arr.max:
return true if arr.count(mx) > 1

Resources