I'm trying to solve this assignment:
Create a Proc that will check whether a given array of elements are nil or not. Use .nil? to evaluate elements. Print 'true' if the elements are nil and 'false' if the element has a [truthy] value.
This is my attempt:
def check_nil(array1)
p = Proc.new {|x| x.nil?}
res= p.call(array1)
if res == true
return "true"
else
return "false"
end
end
array1 = [{},nil,[]]
result = check_nil(array1)
puts "#{result}"
Here actually, the output should be "true" but this code gives "false" as output. Can someone explain the reason?
Your method currently checks whether the given object is nil:
check_nill(nil) #=> "true"
check_nill([]) #=> "false"
check_nill(123) #=> "false"
Note that this object can be anything, so your argument name array1 is somehow misleading for this use case. (you might also want to rename the method to check_nil, i.e. with one l)
Here actually, the output should be "true" [...]
I assume that you actually want to check whether any of the array's elements meets a condition, i.e. if the array contains nil. You can use any? with a block for this. From within the block, you can call your proc:
prc = Proc.new { |x| x.nil? }
[{}, nil, []].any? { |element| prc.call(element) }
#=> true
Which can be shortened by converting the proc to a block:
prc = Proc.new { |x| x.nil? }
[{}, nil, []].any?(&prc)
#=> true
You can even get rid of your custom proc and use Symbol#to_proc instead:
[{}, nil, []].any?(&:nil?)
#=> true
There's also all? which checks whether all elements meet the condition, e.g.:
[{}, nil, []].all?(&:nil?)
#=> false
[nil, nil, nil].all?(&:nil?)
#=> true
If I understand the question correctly, you are trying to check whether all the array elements are nil/blank/false or not. If there is any non-nil value present in the array, you want to return true, else false.
Here is how you can determine this:
array1 = [{}, nil, []]
array1.all?(&:blank?) # this returns true
array2 = [{}, nil, [], 1]
array1.all?(&:blank?) # this returns false
[nil,nil,nil].all? nil No proc.
Truthiness in Ruby
Other issues aside, {} and [] respond to #nil? but are actually truthy since they are neither nil nor false.
[].nil?
#=> false
{}.nil?
#=> false
In Ruby, the only falsey values are nil and false. However, Array and Hash objects do respond to #empty? with a Boolean, which isn't really the same thing as nil.
[].empty?
#=> true
{}.empty?
#=> true
[1].empty?
#=> false
{foo: 1}.empty?
#=> false
Why Use Proc Objects in the First Place
The real point of Proc objects and lambas in Ruby is to create closures that carry their execution context with them even when the original binding has gone out of scope. They aren't really first-class functions, and methods are generally more flexible, so you should generally use a Proc or lambda primarily to pass around a closure that carries variables outside their normal scope.
The Quick Answer: Using a Proc to Return a "Truth Table" Hash
The simplest way to return a "truth table" from a Proc that reports on each element will look something like this:
prc = proc { |array| array.zip(array.map(&:nil?)).to_h }
prc.call [1, 2, nil, 4]
#=> {1=>false, 2=>false, nil=>true, 4=>false}
Getting Fancy with Calling Semantics
In Ruby 3.1.2, you could also create a Proc that can handle an Array or multiple arguments, and return a Hash telling you which of the values are nil.
prc = proc { |*array| array.zip(array.map(&:nil?)).to_h }
prc [1, 2, nil, 4]
#=> {1=>false, 2=>false, nil=>true, 4=>false}
prc[1, 2, nil, 4]
#=> {1=>false, 2=>false, nil=>true, 4=>false}
prc.call(*[1, 2, nil, 4])
#=> {1=>false, 2=>false, nil=>true, 4=>false}
prc.(1, 2, nil, 4)
#=> {1=>false, 2=>false, nil=>true, 4=>false}
Note that the use of variadic calling semantics can be a bit tricky, though, as Proc#[] and Proc#call will treat the collected Array as a single argument unless you splat the Array during the call. For example:
prc = proc { |*array| array.zip(array.map(&:nil?)).to_h }
prc.call [1, 2, nil, 4]
#=> {[1, 2, nil, 4]=>false}
Use a Lambda to Enforce Arity
If you want to force the user to pass only a single argument, and maybe check if it's actually an Array while you're at it, then it's probably better to use a lambda. Lambdas enforce arity, and if you don't trust that you're being passed an Array or a duck-typed object that can #respond_to? typical Array methods, you could do something like this:
check_elements_for_nil = lambda do |arg|
raise TypeError, "expected Array, got #{arg.class}" unless
arg.kind_of? Array
arg.zip(arg.map(&:nil?)).to_h
end
# #raises [ArgumentError] wrong number of arguments
check_elements_for_nil.call(1, 2, 3)
# #raises [TypeError] expected Array, got Hash
check_elements_for_nil.call({foo: 1, bar: 2, baz: 3})
# #return [Hash] truth table for nil values
check_elements_for_nil.call([1, 2, nil, 4])
#=> {1=>false, 2=>false, nil=>true, 4=>false}
More Fun with Stabby Lambdas and Closures
You could make it shorter, too, using a one-liner with a stabby lambda and chaining the input to the lambda definition itself:
->(a) {a.zip(a.map(&:nil?)).to_h}.([1, 2, nil, 4])
#=> {1=>false, 2=>false, nil=>true, 4=>false}
but the practical benefits of this are pretty much zero since you could do the same thing without the lambda. However, since procs and lambdas are closures, it is potentially useful to use something like the following, which demonstrates how to use a lambda with no explicitly-passed arguments:
# method with a hard-coded local variable
#
# #return [lambda] that closes over local variables
def create_a_scope_gate_and_return_a_lambda
array = [1, 2, nil, 4]
->{array.zip(array.map(&:nil?)).to_h}
end
# #note This method doesn't have access to the array
# defined as a local variable in the method above.
# It's like magic!
# #return [Hash] but only because we know what calling
# the lambda will return; pragmatically it could be
# whatever calling +proc_or_lambda+ returns
def call_closure_with_out_of_scope_var(proc_or_lambda)
proc_or_lambda.call
end
a_lambda = create_a_scope_gate_and_return_a_lambda
call_closure_with_out_of_scope_var(a_lambda)
#=> {1=>false, 2=>false, nil=>true, 4=>false}
This works without any explicit arguments to the lambda at all, because the local array variable is in scope when the lambda is defined, and the lambda carries that value with it out through the scope gate of the defining method, and in through the scope gate of a second method.
today, inside my code, if file exist, i try to do something if an Array don't contain specific class, but the test don't work.
def LoadPackagesDatabase
packagesFiles = Dir['Packages/*.rb']
packagesClass = []
packagesFiles.each do |file|
load file
filename = file.sub('.rb', '')
packagesClass.append(Object.const_get(filename.sub('Packages/','')).new)
end
if File.exist?("PackagesDatabase.rb")
$PackagesDatabase = Marshal.load(File.read("PackagesDatabase.rb"))
packagesClass.each do |package|
if !$PackagesDatabase.include?(package.class)
$PackagesDatabase.append(package)
end
end
File.delete("PackagesDatabase.rb")
packagesDatabaseFile = File.open("PackagesDatabase.rb",'w')
packagesDatabaseFile.write(Marshal.dump($PackagesDatabase))
packagesDatabaseFile.close
else
$PackagesDatabase = packagesClass
packagesDatabaseFile = File.open("PackagesDatabase.rb",'w')
packagesDatabaseFile.write(Marshal.dump(packagesClass))
packagesDatabaseFile.close
end
end
I don't would like to test if array contain the same object, but if array contain specific class.
Array#include? does a simple equality check. For more generic checks use any? and all?, and none?. They all take blocks to do the comparison.
if $PackagesDatabase.none? { |item| item.is_a?(package.class) }
$PackagesDatabase.append(package)
end
Note that I'm not using item.class == package.class, that requires an exact match. Instead I'm using is_a?. This ensures subclasses will also match. For example, 23.class == Numeric is false but 23.is_a?(Numeric) is true. Which you use depends on your exact circumstance, but in general you should use is_a? to make your code more flexible.
One could use the mighty Enumerable#grep, which uses the method === for selection. Suppose, for example,
arr = [1, 'cat', 2.3]
One could write, for example:
arr.grep(Float).any? #=> true
arr.grep(Numeric).any? #=> true
arr.grep(Complex).any? #=> false
arr.grep(Array).any? #=> false
In evaluating arr.grep(Float) the method Module#===1 is used to make the following calculations:
Float === 1 #=> false
Float === 'cat' #=> false
Float === 2.3 #=> true
One could instead write
arr.any? { |o| Float === o } #=> true
which has the advantage that it short-circuits the calculation, meaning that the enumeration of the elements of arr is halted if and when the block returns true. By contrast, arr.grep(Float).any? enumerates the entire array.
1. Note that Float.method(:===).owner #=> Module.
I'm using Ruby 2.4. How do I check if all elements of my array satisfy a condition? I have
cond = true
arr.each do |e|
if e.nil? || e.to_i < 5
cond = false
break
end
end
but I feel like there's a more efficient way to do this.
You can rewrite that as
arr.all? { |e| e.to_i >= 5 }
How do I check if all elements of my array satisfy a condition?
Use all?. For example:
[1, 2, 3].all? { |i| i < 4 } #=> true
From the array of string I need to get string which starts with age- followed by maximum of 2 digit number and optional '+' sign.
Ex: age-1, age-22, age55, age-1+, age-15+
Following is my array:
arr = ["vintage-colllections","age-5"]
or
arr = ["vintage-colllections","age-51+"]
I will extract age "age-5" or "age-51+" from the array.
I tried following things:
arr.find {|e| e.include?"age-"}
Works well for other scenarios but in above the 1st element of array also includes (vint)age- failing there.
arr.find { |e| /age-\d*\+?/ =~ e}
Works fine but I am trying to avoid regex.
Is there any other better approach ?.
Any suggestions are welcome.
Use start_with?:
arr.find { |e| e.start_with?("age-") }
I must grit my teeth to not use a regex, but here goes. I assume the question is as described in a comment I left on the question.
def find_str(arr)
arr.map { |str| str_match(str) }.compact
end
def str_match(str)
return nil unless str[0,4] == "age-"
last = str[-1] == '+' ? -2 : -1
Integer(str[4..last]) rescue return nil
str
end
find_str ["cat", "age-5"] #=> ["age-5"]
find_str ["cat", "age-51+"] #=> ["age-51+"]
find_str ["cat", "age-5t1+"] #=> []
find_str ["cat", "xage-51+"] #=> []
I want to find out if an Array contains only unique (distinct) entries.
[1,2,3,2].what_goes_here? #=> false
[1,2,3,4].what_goes_here? #=> true
I don't want to manipulate the original array, so
uniq! { |item| ... } → ary or nil
...
Returns nil if no changes are made (that is, no duplicates are found).
seems not the right solution. Otherwise I could probably do [1,2,3,4].uniq!.nil?.
I prefer an elegant, readable, over a convoluted-but-better-performing solution.
array.uniq.size == array.size
array.to_set.size == array.size
array.all? { |element| array.count(element) == 1 }
a = [1, 2, 3, 2]
a.dup.uniq!.nil? # false
a.uniq == a # false
a & a == a # false
a | a == a # false
If the array is large you may want to use a method that examines elements sequentially and stops when a dup is found, rather than traversing the entire array. You can do that efficiently with a hash:
def dups?(arr)
arr.each_with_object({}) do |e,h|
return true if h[e]
h[e] = true
end
false
end
dups? [1,2,3,1,4,5,6,3,1,8,9]
#=> true
dups? [1,2,3,4,5,6,-6,-5,-4,-3,-2,-1]
#=> false
...or with a set:
require 'set'
def dups?(arr)
arr.each_with_object(Set.new) do |e,s|
return true if s.include? e
s << e
end
false
end