Deleting EmbeddedDocuments with Mongo Mapper - ruby

I have mongo_mapper set up like so:
class Person
include MongoMapper::Document
many :pets
end
class Pet
include MongoMapper::EmbeddedDocument
key :animal, String
key :name, String
key :colour, String
end
# Create a person
me = Person.new
# Add pets to the person
me.pets << Pet.new(:animal => 'dog',:name => 'Mr. Woofs', :colour => 'golden')
me.pets << Pet.new(:animal => 'cat', :name => 'Kitty', :colour => 'black')
me.pets << Pet.new(:animal => 'cat', :name => 'Freckles', :colour => 'black')
me.pets << Pet.new(:animal => 'cat', :name => 'Fluffy', :colour => 'tabby')
I know I can delete all pets very simply (me.pets works as an array but also calls back)
# Delete all pets
me.pets.clear
I also know that I could delete all black cats by doing this:
# Delete black cats
me.pets.delete_if {|pet| pet.animal == 'cat' and pet.colour = 'black'}
But that seems like it'll take a very long time if there are a large number of pets to iterate through.
I feel like there should be a way to select only the black cats and then clear that array instead. Is there such a way?

try something like this, no idea if this'll work but worth a shot.
me.pets.all(:animal => "cat", :colour => "black").clear
To be honest though I think you are worrying about this for nothing. Usually array manipulation are plenty fast.

Related

New hash from array of hashes

The objective of the code below is to produce a hash with the keys being the :id field of
the hashes in original_array, and the values being all elements in original_array which have that :id.
original_array = [
{:id => '123', :name => 'test'},
{:id => '123', :name => 'another test'},
{:id => '456', :name => 'yet another test'}
]
new_hash = {}
original_array.each do |a|
new_hash[a[:id]] = original_array.select {|x| x[:id] == a[:id]}
end
My code does that, but there must be some better way to do it, ideally where the hash can be created in one step. If anyone can suggest and explain one (in the hope that I might improve my understanding of this sort of thing), then it would be appreciated.
This should do it
new_hash = original_array.group_by{|h| h[:id]}
Documentation: Enumerable#group_by.

An efficient way of custom tag parsing in ruby

I have a hash like:
{:name => 'foo', :country => 'bar', :age => 22}
I also have a string like
Hello ##name##, you are from ##country## and your age is ##age##. I like ##country##
Using above hash, I want to parse this string and substitute the tags with corresponding values. So after parsing, the string will look like:
Hello foo, you are from bar and your age is 22. I like bar
Do you recommend taking help of regex to parse it? In that case if I have 5 values in the hash, then I would have to traverse through string 5 times and each time parsing one tag. I don't think that is a good solution. Is there any better solution?
Here is my solution to the problem:
h = {:name => 'foo', :country => 'bar', :age => 22}
s = "Hello ##name##, you are from ##country## and your age is ##age##. I like ##country##}"
s.gsub!(/##([a-zA-Z]*)##/) {|not_needed| h[$1.to_sym]}
It generally makes a single pass using regex and does the replacement I think you need.
It looks like there is a solution depending on what version of ruby you are using. For 1.9.2 you can use a hash, shown here: https://stackoverflow.com/a/8132638/1572626
The question is generally similar, though, so read the other comments as well: Ruby multiple string replacement
You can use String#gsub with a block:
h = {:name => 'foo', :country => 'bar', :age => 22}
s = 'Hello ##name##, you are from ##country## and your age is ##age##. I like ##country##'
s.gsub(/##(.+?)##/) { |match| h[$1.to_sym] }

Question on Ruby collect method

I have an array of hashes
Eg:
cars = [{:company => "Ford", :type => "SUV"},
{:company => "Honda", :type => "Sedan"},
{:company => "Toyota", :type => "Sedan"}]
# i want to fetch all the companies of the cars
cars.collect{|c| c[:company]}
# => ["Ford", "Honda", "Toyota"]
# i'm lazy and i want to do something like this
cars.collect(&:company)
# => undefined method `company'
I was wondering if there is a similar shortcut to perform the above.
I believe your current code cars.collect{|c| c[:company]} is the best way if you're enumerating over an arbitrary array. The method you would pass in via the & shortcut would have to be a method defined on Hash since each object in the array is of type Hash. Since there is no company method defined for Hash you get the "undefined method 'company'" error.
You could use cars.collect(&:company) if you were operating on an Array of Cars though, because each object passed into the collect block would be of type Car (which has the company method available). So maybe you could modify your code so that you use an array of Cars instead.
You could convert the hashes to OpenStructs.
require 'ostruct'
cars = [{:company => "Ford", :type => "SUV"},
{:company => "Honda", :type => "Sedan"},
{:company => "Toyota", :type => "Sedan"}]
cars = cars.map{|car| OpenStruct.new(car)}
p cars.map( &:company )
#=> ["Ford", "Honda", "Toyota"]
It's impossible to use in your case, because in collect you use method [] and argument :company. The construction &:company takes labels :company and converts to Proc, so it's only one argument - the name of method.
Unfortunately Ruby hashes can't do that. Clojure maps on the other hand have functions for each key which return the corresponding value, which would be easy enough to do if you are so inclined (you should also add the corresponding respond_to? method):
>> class Hash
.. def method_missing(m)
.. self.has_key?(m) ? self[m] : super
.. end
.. end #=> nil
>> cars.collect(&:company) #=> ["Ford", "Honda", "Toyota"]
>> cars.collect(&:compay)
NoMethodError: undefined method `compay' for {:type=>"SUV", :company=>"Ford"}:Hash
Note: I'm not advising this, I'm just saying it's possible.
Another horrible monkeypatch you shouldn't really use:
class Symbol
def to_proc
if self.to_s =~ /bracket_(.*)/
Proc.new {|x| x[$1.to_sym]}
else
Proc.new {|x| x.send(self)}
end
end
end
cars = [{:company => "Ford", :type => "SUV"},
{:company => "Honda", :type => "Sedan"},
{:company => "Toyota", :type => "Sedan"}]
cars.collect(&:bracket_company)

Using redis' list data structure or a serialized ruby hash for storing data

Greetings,
I want to store some data in a redis db and don't know which way I should go. The data is equivalent to something like an address with the variables name, street and number. They will be stored under the lower cased name as key, there won't be doublets.
Now, should I save it as a list or should I serialize the hash ({:name => 'foo', :street => 'bar', :number => 'baz'} for example) with JSON/Marshall and simply store that?
Regards
Tobias
Using an encoded json object is a pretty good idea. You can see some examples in hurl — check out how the models are saved.
Redis hashes are nice too, especially if you need atomic operations on hash values.
Also you can use something like Nest to help you DRY up your keys:
addresses = Nest.new("Address", Redis.new)
this_address = addresses[1]
# => "Address:1"
this_address.hset(:name, "foo")
this_address.hset(:street, "bar")
this_address.hgetall
# => {"name" => "foo", "street" => "bar"}
If you need something more advanced, there's Ohm, which maps Ruby classes to Redis:
class Address < Ohm::Model
attribute :name
attribute :street
attribute :number
end
# Create
Address.create(:name => "foo", :street => "bar")
# Find by ID
Address[1]
# Find all addresses with name "foo"
class Address < Ohm::Model
attribute :name
attribute :street
attribute :number
index :name
end
Address.find(:name => "foo")
# => Array-like with all the Address objects

Ruby: Create hash with default keys + values of an array

I believe this has been asked/answered before in a slightly different context, and I've seen answers to some examples somewhat similar to this - but nothing seems to exactly fit.
I have an array of email addresses:
#emails = ["test#test.com", "test2#test2.com"]
I want to create a hash out of this array, but it must look like this:
input_data = {:id => "#{id}", :session => "#{session}",
:newPropValues => [{:key => "OWNER_EMAILS", :value => "test#test.com"} ,
{:key => "OWNER_EMAILS", :value => "test2#test2.com"}]
I think the Array of Hash inside of the hash is throwing me off. But I've played around with inject, update, merge, collect, map and have had no luck generating this type of dynamic hash that needs to be created based on how many entries in the #emails Array.
Does anyone have any suggestions on how to pull this off?
So basically your question is like this:
having this array:
emails = ["test#test.com", "test2#test2.com", ....]
You want an array of hashes like this:
output = [{:key => "OWNER_EMAILS", :value => "test#test.com"},{:key => "OWNER_EMAILS", :value => "test2#test2.com"}, ...]
One solution would be:
emails.inject([]){|result,email| result << {:key => "OWNER_EMAILS", :value => email} }
Update: of course we can do it this way:
emails.map {|email| {:key => "OWNER_EMAILS", :value => email} }

Resources