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.
Related
Ruby max_by method finds the maximal element form an array. Sometimes the maximal elements are with multipicity, in this case max_by chooses only one of them, seemingly arbitrarily. When I need all of them, I use this approach currently, to find the maximal values in an array of arrays:
sorted=ary.sort_by{|a,b| b}.reverse
max_score=sorted.first[1]
t=sorted.take_while{|z| z[1]==max_score}
But how could I monkey-patch the Array class with a "maxes_by" method, which accept a block, similarly to max_by, and returns an array of the maximal values?
Without writing a new, optimized method that returns the expected output you can simply combine max_by and select:
maximum = array.max_by { |element| element[1] }
t = array.select { |element| element[1] == maximum[1] }
Another option might be to group all elements by the value in question (with group_by) and then just pick the list with the max value.
lists = array.group_by { |element| element[1] }
lists[lists.keys.maximum]
It's worth a mention that the task could be done in a single pass through the array, or more generally, in a single pass through any collection whose class includes Enumerable.
module Enumerable
def max_by_all
return each unless block_given?
last_yield = nil
each_with_object([]) do |e,a|
ye = yield(e)
case last_yield.nil? ? -1 : last_yield <=> ye
when -1
a.replace([e])
last_yield = ye
when 0
a << e
end
end
end
end
arr = [2, 4, 3, 4, 1, 2, 5, 3, 5, 1]
arr.max_by_all(&:itself)
#=> [5, 5]
arr = ["style", "assets", "misty", "assist", "corgi", "bossy", "bosses", "chess"]
arr.max_by_all { |s| s.count('s') }
#=> ["assets", "assist", "bosses"]
h = { a: 1, b: 3, c: 2, d: 3, e: 1 }
h.max_by_all(&:last)
#=> [[:b, 3], [:d, 3]]
arr = [1, 2, 3]
arr.max_by_all.map { |n| 2*n }
#=> [2, 4, 6]
In the last example max_by_all has no block and therefore returns an enumerator which merely enumerates the elements of self. This behaviour may seem pointless but I've provided for it (the line return each unless block_given?) to mimic the behaviour of Enumerable#max_by when no block is provided.
Using Monkey-Patch
class Array
def maxes_by
maximal = max_by { |x| yield(x) }
select { |x| yield(x) == yield(maximal) }
end
end
Usage
> ['house', 'car', 'mouse'].maxes_by { |x| x.length }
=> ['house', 'mouse']
But I don't recommend to monkey patch the Array class, this practice is dangerous and can potentially lead to undesirable effects on your system.
For our good, ruby language provides a nice feature to overcome this problem, the Refinements, which is a safe way for monkey patching on ruby.
To simplify, with the Refinements you can monkey patch the Array class and the changes will only be available inside the scope of the class that is using the refinement! :)
You can use the refinement inside the class you are working on and you are ready to go.
Using Refinements
module MaxesByRefinement
refine Array do
def maxes_by
maximal = max_by { |x| yield(x) }
select { |x| yield(x) == yield(maximal) }
end
end
end
class MyClass
using MaxesByRefinement
def test
a = %w(house car mouse)
a.maxes_by { |x| x.length } # maxes_by is available here!
end
end
Usage
> MyClass.new.test
=> ['house', 'mouse']
Given an array with only odd counts:
[1,nil,nil]
[1,nil,Module,nil,2]
[1,Class.new,nil]
I would like to determine if there are nils or more non-nils. The approach I used was to make everything either true or false first. And then to determine if there are more true or false values:
[ 1,nil,nil,nil,2,3].collect {|val| !!!val }.max
#=> ArgumentError: comparison of TrueClass with false failed
The max method does not want to play nice with booleans. How can I accomplish this?
Now this might not be the best approach to determine whether there are more nils or non-nils, but this is the approach that I used.
Given an array with only odd counts
If by that you mean that there will always be the nonequal amount of truthy/falsey values in an array, then, first of all, [] is not a valid input.
And here's the solution:
def truthy?(array)
falsey, truthy = array.partition(&:!)
truthy.size > falsey.size
end
You can go with oneliner if you prefer:
def truthy?(array)
array.partition(&:!).max_by(&:size).any?
end
Spec:
truthy?([1,nil,nil]) #=> false
truthy?([1,nil,nil,nil,2]) #=> false
truthy?([1,4,nil]) #=> true
truthy?([1,nil,nil]) #=> false
truthy?([1,nil,Module,nil,2]) #=> true
truthy?([1,Class.new,nil]) #=> true
It uses
Enumerable#partition method;
BasicObject#! method.
If you indeed intended to only calculate nils, not falsey values (as it was stated in the OP):
def more_nils?(array)
array.partition(&:nil?).max_by(&:size).none?
end
Spec:
more_nils?([1,nil,nil]) #=> true
more_nils?([1,nil,nil,nil,2]) #=> true
more_nils?([1,4,nil]) #=> false
more_nils?([1,nil,nil]) #=> true
more_nils?([1,nil,Module,nil,2]) #=> false
more_nils?([1,Class.new,nil]) #=> false
It uses Object#nil? method.
Inspired by #pjs's answer:
array.sum { |el| el.nil? ? -1 : 1 }.negative?
Even simpler ( from #SagarPandya's comment)
array.count(nil) > array.compact.count
A fairly straightforward solution would be:
def truthy?(ary)
ary.map { |bool| bool ? 1 : -1 }.sum > 0
end
Map entries to +/-1 based on their truthiness, sum, and see whether the sum is positive or negative.
This can deal with empty arrays, it returns false in that case.
Here another one:
if array.size > 2*array.compact.size
# We have more nil than non-nil
end
Assuming that falsy values are nil and false, and everything else is truthy (as conditional statements do), you can leverage Object#itself with Array#select.
irb(main):013:0> ary = [1,nil,nil,false,2]
=> [1, nil, nil, false, 2]
irb(main):014:0> ary.select(&:itself).length
=> 2
irb(main):015:0> ary.reject(&:itself).length
=> 3
I'm currently learning ruby and I wrote this piece of code :
def multi_gen
s = []
for i in (3..10)
if i%3 == 0 || i%5 == 0
s<<i
end
end
return s
end
puts multi_gen
def rec_sum(num_arr)
if num_arr == []
return 0
else
num_arr.first + rec_sum(num_arr.shift)
end
end
puts rec_sum(multi_gen)
That should return the sum of all 3 and 5 multiples up to 1000.
But I get an error :
myrbfile.rb:17:in `rec_sum': undefined method `first' for 3:Fixnum (NoMethodError)
from villani.rb:17:in `rec_sum'
from villani.rb:21:in `<main>'
But when I re-write it like this :
def multi_gen
s = []
for i in (3..10)
if i%3 == 0 || i%5 == 0
s<<i
end
end
return s
end
puts multi_gen
def rec_sum(num_arr)
if num_arr == []
return 0
else
num_arr[0] + rec_sum(num_arr[1..num_arr.last])
end
end
puts rec_sum(multi_gen)
I don't get the error.
So why is my first rec_sum functions interpretting my Array as a Fixnum in the first case?
The issue is in the recursive call:
rec_sum(num_arr.shift)
Array#shift returns the shifted element, not the remaining array. You should explicitly pass the array as an argument to recursive call:
rec_sum(num_arr[1..-1])
or
rec_sum(num_arr.tap(&:shift))
The latter would [likely] be looking too cumbersome for the beginner, but it’s a very common rubyish approach: Object#tap yields the receiver to the block, returning the receiver. Inside a block (num_arr.tap(&:shift) is a shorthand for num_arr.tap { |a| a.shift } we mutate the array by shifting the element out, and it’s being returned as a result.
mudasobwa already explained why using shift doesn't give the expected result. Apart from that, your code is somehow unidiomatic.
In multi_gen you are creating an empty array and append elements to it using a for loop. You rarely have to populate an array manually. Instead, you can usually use one of Ruby's Array or Enumerable methods to generate the array. select is a very common one – it returns an array containing the elements for which the given block returns true:
(1..1000).select { |i| i % 3 == 0 || i % 5 == 0 }
#=> [3, 5, 6, 9, 10, 12, ...]
In rec_sum, you check if num_arr == []. Although this works, you are creating an empty throw-away array. To determine whether an array is empty, you should call its empty?:
if num_arr.empty?
# ...
end
To get the remaining elements from the array, you use:
num_arr[1..num_arr.last]
which can be abbreviated by passing a negative index to []:
num_arr[1..-1]
There's also drop which might look a little nicer:
num_arr[0] + rec_sum(num_arr[1..-1])
# vs
num_arr.first + rec_sum(num_arr.drop(1))
Another option to get first and remaining elements from an array is Ruby's array decomposition feature (note the *):
def rec_sum(num_arr)
if num_arr.empty?
0
else
first, *remaining = num_arr
first + rec_sum(remaining)
end
end
You could also consider using a guard clause to return from the method early:
def rec_sum(num_arr)
return 0 if num_arr.empty?
first, *remaining = num_arr
first + rec_sum(remaining)
end
Writing recursive methods is great for learning purposed, but Ruby also has a built-in sum method:
multi_gen.sum #=> 234168
or – since you are using an older Ruby version – inject:
multi_gen.inject(0, :+) #=> 234168
I have this code here
string.split(/(\w{1,}=)/).each_slice(1).map { |i| items << i }
items.map! do |i|
i = i << str if i.to_s =~ /\w{1,}=/
end
puts items*''
And I want to modify certain items in the array based on regex, then return the full array with the modified items in it. This only returns the modified items. How do I achieve what I'm looking for?
EDIT: Ok, so say I'm trying to split a link using this regex:
page.php?site=blah&id=1
The link is split and added to the array which now contains
page.php?
site=
blah&
id=
1
What I want to do is append some value to the end of the elements ending with a =. This way, when I return the modified array as a string it would output like this:
page.php?site=(newval)&id=(newval)
You have several undefined variables in your example, which is very sloppy.
each_slice(1) is equivalent to each(), so it's not clear why you are using each_slice(1). In any case, both each() and map() step through the items in an Array one by one, but each() returns the original Array unchanged. On the other hand, you use map() when you want to create a new Array that contains changes to the items.
In the regex /\w{1,}/, there is a shortcut for the quantifier {1, }, and it's: +, so most people would write the regex as /\w+/, where + means 1 or more.
I want to modify certain items in the array based on regex, then
return the full array with the modified items in it.
Here is an example:
results = [1, 2, 3].map do |num|
if num == 2
num + 4
else
num - 1
end
end
p results
--output:--
[0, 6, 2]
Your current attempt with map() doesn't return anything if the conditional fails. Note how the example above returns something both when the condition fails AND when the condition succeeds. map() replaces an item with whatever is returned for that item.
Now look at this example:
results = [1, 2, 3].map do |num|
if num == 2
num + 4
end
end
p results
--output:--
[nil, 6, nil]
If you don't return something for an item, then map() will use nil for that item. In the example, if the condition num == 2 is true then num+4 is returned--but if num == 2 is false, nothing is returned.
Edit:
words = %w[
page.php?
site=
blah&
id=
1
] #=> words = ["page.php?", "site=", "blah&", "id=", "1"]
suffix = 'hello'
results = words.map do |word|
if word.end_with?('=')
"#{word}#{suffix}"
else
word
end
end
p results
--output:--
["page.php?", "site=hello", "blah&", "id=hello", "1"]
Instead of parsing a URL with a regex, have you considered using the addressable gem?
require 'addressable/uri'
uri = Addressable::URI.parse('page.php?site=blah&id=1&bar')
uri.query_values = uri.query_values.map do |k, v|
[k, v.is_a?(String) ? v << 'foo' : v]
end
puts uri.to_s # => page.php?site=blahfoo&id=1foo&bar
This won't handle very complex query parameters (it will just pass them through).
You can use respond_to? :sub! and v.sub! /$/, 'foo' instead of checking types if that makes you uneasy. (I wouldn't use :<< or :concat because those are valid methods for Arrays.)
I have an array, and I want to make a hash so I can quickly ask "is X in the array?".
In perl, there is an easy (and fast) way to do this:
my #array = qw( 1 2 3 );
my %hash;
#hash{#array} = undef;
This generates a hash that looks like:
{
1 => undef,
2 => undef,
3 => undef,
}
The best I've come up with in Ruby is:
array = [1, 2, 3]
hash = Hash[array.map {|x| [x, nil]}]
which gives:
{1=>nil, 2=>nil, 3=>nil}
Is there a better Ruby way?
EDIT 1
No, Array.include? is not a good idea. Its slow. It does a query in O(n) instead of O(1). My example array had three elements for brevity; assume the actual one has a million elements. Let's do a little benchmarking:
#!/usr/bin/ruby -w
require 'benchmark'
array = (1..1_000_000).to_a
hash = Hash[array.map {|x| [x, nil]}]
Benchmark.bm(15) do |x|
x.report("Array.include?") { 1000.times { array.include?(500_000) } }
x.report("Hash.include?") { 1000.times { hash.include?(500_000) } }
end
Produces:
user system total real
Array.include? 46.190000 0.160000 46.350000 ( 46.593477)
Hash.include? 0.000000 0.000000 0.000000 ( 0.000523)
If all you need the hash for is membership, consider using a Set:
Set
Set implements a collection of unordered values with no
duplicates. This is a hybrid of Array's intuitive inter-operation
facilities and Hash's fast lookup.
Set is easy to use with Enumerable objects (implementing
each). Most of the initializer methods and binary operators accept
generic Enumerable objects besides sets and arrays. An
Enumerable object can be converted to Set using the
to_set method.
Set uses Hash as storage, so you must note the following points:
Equality of elements is determined according to Object#eql? and Object#hash.
Set assumes that the identity of each element does not change while it is stored. Modifying an element of a set will render the set to an
unreliable state.
When a string is to be stored, a frozen copy of the string is stored instead unless the original string is already frozen.
Comparison
The comparison operators <, >, <= and >= are implemented as
shorthand for the {proper_,}{subset?,superset?} methods. However, the
<=> operator is intentionally left out because not every pair of
sets is comparable. ({x,y} vs. {x,z} for example)
Example
require 'set'
s1 = Set.new [1, 2] # -> #<Set: {1, 2}>
s2 = [1, 2].to_set # -> #<Set: {1, 2}>
s1 == s2 # -> true
s1.add("foo") # -> #<Set: {1, 2, "foo"}>
s1.merge([2, 6]) # -> #<Set: {1, 2, "foo", 6}>
s1.subset? s2 # -> false
s2.subset? s1 # -> true
[...]
Public Class Methods
new(enum = nil)
Creates a new set containing the elements of the given enumerable
object.
If a block is given, the elements of enum are preprocessed by the
given block.
try this one:
a=[1,2,3]
Hash[a.zip]
You can do this very handy trick:
Hash[*[1, 2, 3, 4].map {|k| [k, nil]}.flatten]
=> {1=>nil, 2=>nil, 3=>nil, 4=>nil}
If you want to quickly ask "is X in the array?" you should use Array#include?.
Edit (in response to addition in OP):
If you want speedy look up times, use a Set. Having a Hash that points to all nils is silly. Conversion is an easy process too with Array#to_set.
require 'benchmark'
require 'set'
array = (1..1_000_000).to_a
set = array.to_set
Benchmark.bm(15) do |x|
x.report("Array.include?") { 1000.times { array.include?(500_000) } }
x.report("Set.include?") { 1000.times { set.include?(500_000) } }
end
Results on my machine:
user system total real
Array.include? 36.200000 0.140000 36.340000 ( 36.740605)
Set.include? 0.000000 0.000000 0.000000 ( 0.000515)
You should consider just using a set to begin with, instead of an array so that a conversion is never necessary.
I'm fairly certain that there isn't a one-shot clever way to construct this hash. My inclination would be to just be explicit and state what I'm doing:
hash = {}
array.each{|x| hash[x] = nil}
It doesn't look particularly elegant, but it's clear, and does the job.
FWIW, your original suggestion (under Ruby 1.8.6 at least) doesn't seem to work. I get an "ArgumentError: odd number of arguments for Hash" error. Hash.[] expects a literal, even-lengthed list of values:
Hash[a, 1, b, 2] # => {a => 1, b => 2}
so I tried changing your code to:
hash = Hash[*array.map {|x| [x, nil]}.flatten]
but the performance is dire:
#!/usr/bin/ruby -w
require 'benchmark'
array = (1..100_000).to_a
Benchmark.bm(15) do |x|
x.report("assignment loop") {hash = {}; array.each{|e| hash[e] = nil}}
x.report("hash constructor") {hash = Hash[*array.map {|e| [e, nil]}.flatten]}
end
gives
user system total real
assignment loop 0.440000 0.200000 0.640000 ( 0.657287)
hash constructor 4.440000 0.250000 4.690000 ( 4.758663)
Unless I'm missing something here, a simple assignment loop seems the clearest and most efficient way to construct this hash.
Rampion beat me to it. Set might be the answer.
You can do:
require 'set'
set = array.to_set
set.include?(x)
Your way of creating the hash looks good. I had a muck around in irb and this is another way
>> [1,2,3,4].inject(Hash.new) { |h,i| {i => nil}.merge(h) }
=> {1=>nil, 2=>nil, 3=>nil, 4=>nil}
I think chrismear's point on using assignment over creation is great. To make the whole thing a little more Ruby-esque, though, I might suggest assigning something other than nil to each element:
hash = {}
array.each { |x| hash[x] = 1 } # or true or something else "truthy"
...
if hash[376] # instead of if hash.has_key?(376)
...
end
The problem with assigning to nil is that you have to use has_key? instead of [], since [] give you nil (your marker value) if the Hash doesn't have the specified key. You could get around this by using a different default value, but why go through the extra work?
# much less elegant than above:
hash = Hash.new(42)
array.each { |x| hash[x] = nil }
...
unless hash[376]
...
end
Maybe I am misunderstanding the goal here; If you wanted to know if X was in the array, why not do array.include?("X") ?
Doing some benchmarking on the suggestions so far gives that chrismear and Gaius's assignment-based hash creation is slightly faster than my map method (and assigning nil is slightly faster than assigning true). mtyaka and rampion's Set suggestion is about 35% slower to create.
As far as lookups, hash.include?(x) is a very tiny amount faster than hash[x]; both are twice as a fast as set.include?(x).
user system total real
chrismear 6.050000 0.850000 6.900000 ( 6.959355)
derobert 6.010000 1.060000 7.070000 ( 7.113237)
Gaius 6.210000 0.810000 7.020000 ( 7.049815)
mtyaka 8.750000 1.190000 9.940000 ( 9.967548)
rampion 8.700000 1.210000 9.910000 ( 9.962281)
user system total real
times 10.880000 0.000000 10.880000 ( 10.921315)
set 93.030000 17.490000 110.520000 (110.817044)
hash-i 45.820000 8.040000 53.860000 ( 53.981141)
hash-e 47.070000 8.280000 55.350000 ( 55.487760)
Benchmarking code is:
#!/usr/bin/ruby -w
require 'benchmark'
require 'set'
array = (1..5_000_000).to_a
Benchmark.bmbm(10) do |bm|
bm.report('chrismear') { hash = {}; array.each{|x| hash[x] = nil} }
bm.report('derobert') { hash = Hash[array.map {|x| [x, nil]}] }
bm.report('Gaius') { hash = {}; array.each{|x| hash[x] = true} }
bm.report('mtyaka') { set = array.to_set }
bm.report('rampion') { set = Set.new(array) }
end
hash = Hash[array.map {|x| [x, true]}]
set = array.to_set
array = nil
GC.start
GC.disable
Benchmark.bmbm(10) do |bm|
bm.report('times') { 100_000_000.times { } }
bm.report('set') { 100_000_000.times { set.include?(500_000) } }
bm.report('hash-i') { 100_000_000.times { hash.include?(500_000) } }
bm.report('hash-e') { 100_000_000.times { hash[500_000] } }
end
GC.enable
If you're not bothered what the hash values are
irb(main):031:0> a=(1..1_000_000).to_a ; a.length
=> 1000000
irb(main):032:0> h=Hash[a.zip a] ; h.keys.length
=> 1000000
Takes a second or so on my desktop.
If you're looking for an equivalent of this Perl code:
grep {$_ eq $element} #array
You can just use the simple Ruby code:
array.include?(element)
Here's a neat way to cache lookups with a Hash:
a = (1..1000000).to_a
h = Hash.new{|hash,key| hash[key] = true if a.include? key}
Pretty much what it does is create a default constructor for new hash values, then stores "true" in the cache if it's in the array (nil otherwise). This allows lazy loading into the cache, just in case you don't use every element.
This preserves 0's if your hash was [0,0,0,1,0]
hash = {}
arr.each_with_index{|el, idx| hash.merge!({(idx + 1 )=> el }) }
Returns :
# {1=>0, 2=>0, 3=>0, 4=>1, 5=>0}