I receive a param and want it to be either a string like this :
"abc,efg"
or an Array like this
["abc","efg"]
In the first case I want to convert it into an Array, what would be the good way ?
Here is what I thought
if params[:ids] && params[:ids].is_a? Array
ids = params[:ids]
else if params[:ids]
ids = params[:ids].split(",")
I'd use a ternary for this to keep it simple and on one line:
ids = params[:ids].is_a?(String) ? params[:ids].split(',') : params[:ids]
I've reversed the order so you don't get an undefined method error if you try calling split on nil should params[:ids] be missing.
Array.wrap(params[:ids]).map{|x| x.split(',')}.flatten
Apologies for piling on. But I thought I would offer a slight tweak to the answer proposed by SickLickWill (which doesn't quite handle the Array case correctly):
ids = params[:id].split(',').flatten
This will handle the String case just fine:
:001 > params = {id: "abc,efg"}
:002 > ids = params[:id].split(',').flatten
=> ["abc", "efg"]
As well as the Array case:
:003 > params = {id: ["abc","efg"]}
:004 > ids = params[:id].split(',').flatten
=> ["abc", "efg"]
If there's any chance the id param will be nil, then this barfs:
:005 > params = {}
=> {}
:006 > ids = params[:id].split(',').flatten
NoMethodError: undefined method `split' for nil:NilClass
So, you could put in a conditional test:
:007 > ids = params[:id].split(',').flatten if params[:id]
=> nil
Or, use try:
:008 > ids = params[:id].try(:split, ',').try(:flatten)
=> nil
You miss end tag and you have wrong else if and you can delete the check of params[:ids] because if :ids key do not exist is_a? return NilClass
I think you can do this
ids = if params[:ids].is_a? Array
params[:ids]
elsif params[:ids]
params[:ids].split(",")
end
I think the shortest way would be to use .try. It saves you from writing out an if-then-else.
params_id = params[:id].try(:split, ',')
Related
Question I have a custom divide and conquer array sorter that I would like to use. This all works well until I try to use it on an array in my controller I get this message.. NoMethodError (undefined method '<=' for #<Entry:0x0000000ac7d850>): Any help would be greatly appreciated thank you!
here is my Entry model with the mergesort method I am calling in my controller.
def self.mergesort(container)
return container if (container.size <= 1)
mid = container.size / 2
left = container[0...mid]
right = container[mid...container.size]
merge(mergesort(left), mergesort(right))
end
def self.merge(left, right)
sorted = []
until left.empty? or right.empty?
(left.first <= right.first) ? sorted << left.shift : sorted << right.shift
end
sorted + left + right
end
Here is my Entry controller where I am trying to call it.
def pending_sort
#ent_sort = Entry.where("section = ? and approve_disapprove = ?", #mgr_section, '3')
#ent = Entry.mergesort(#ent_sort)
end
You probably have a nil for the first element of either left or right.
irb(main):001:0> left = []
=> []
irb(main):002:0> right = [1]
=> [1]
irb(main):003:0> left.first
=> nil
irb(main):004:0> left.first <= right.first
NoMethodError: undefined method `<=' for nil:NilClass
from (irb):4
from /usr/bin/irb:11:in `<main>'
You can fix the error by casting the nil to a different value. For example, if the values you are comparing are always integers you can change the following line:
(left.first <= right.first) ? sorted << left.shift : sorted << right.shift
to this:
(left.first.to_i <= right.first.to_i) ? sorted << left.shift : sorted << right.shift
But think about how it will affect your functionality... it may break something if it isn't what you actually want to do.
I'm new in rails and need to clear one question:
for example my method return such data:
#<Article ART_ID: 1151754, ART_ARTICLE_NR: "0 281 002 757", ART_SUP_ID: 30, ART_DES_ID: nil, ART_COMPLETE_DES_ID: 62395, ART_CTM: nil, ART_PACK_SELFSERVICE: 0, ART_MATERIAL_MARK: 0, ART_REPLACEMENT: 0, ART_ACCESSORY: 0, ART_BATCH_SIZE1: nil, ART_BATCH_SIZE2: nil, datetime_of_update: "2012-09-25 17:49:18">
or array, not only one object: how could use each func then?
for example:
articles = ArtLookup.search_strong_any_kind_without_brand(params[:article_nr].gsub(/[^0-9A-Za-z]/, ''))
binding.pry
if articles.present?
articles.each do |a|
#all_parts_result <<
{
analogue_manufacturer_name: a.supplier.SUP_BRAND,
analogue_code: a.ART_ARTICLE_NR,
delivery_time_min: '',
delivery_time_max: '',
min_quantity: '',
product_name: a.art_name,
quantity: '',
price: '',
distributor_id: '',
link_to_tecdoc: a.ART_ID
}
end
end
now i get errors like
`undefined method `each' for `#<Article:0x007f6554701640>
i think it is becouse i have sometimes one object, sometimes 10, and sometime 0.
how is it beatifull and right to do in rails?
Your search_strong_any_kind_without_brand method is looping through your articles based on the search condition. If the article matches then you are setting #art_concret to the match and then returning the match. However, you're not finding all matches, just the last one.
.
loop
#art_concret = art
end
.
return #art_concret
If you set the #art_concret as an array and inject results into this instance variable, then you will have the resulting search in array form. However, keep in mind that this does kind of break the ActiveRecord ORM as you would be returning a simple array and not an ActiveRecord Relation array.
def self.search_strong_any_kind_without_brand(search)
search_condition = search.upcase
#art_concret = []
#search = find(:all, :conditions => ['MATCH (ARL_SEARCH_NUMBER) AGAINST(? IN BOOLEAN MODE)', search_condition])
#articles = Article.find(:all, :conditions => ["ART_ID in (?)", #search.map(&:ARL_ART_ID)])
#binding.pry
#articles.each do |art|
if art.ART_ARTICLE_NR.gsub(/[^0-9A-Za-z]/, '') == search
#art_concret << art
end
end
return #art_concret
end
If you want to keep the code a bit cleaner then use select on your matching condition instead of looping through each article in #articles.
def self.search_strong_any_kind_without_brand(search)
search_condition = search.upcase
#search = find(:all, :conditions => ['MATCH (ARL_SEARCH_NUMBER) AGAINST(? IN BOOLEAN MODE)', search_condition])
#articles = Article.find(:all, :conditions => ["ART_ID in (?)", #search.map(&:ARL_ART_ID)])
#binding.pry
return #articles.select { |art| art.ART_ARTICLE_NR.gsub(/[^0-9A-Za-z]/, '') == search }
end
Unrelated: is there a reason why you're using instance variables in search_strong_any_kind_without_brand?
I think the right thing to do is to make sure your method always returns an array (or enumerable).
looking at the code you posted in to pastebin I would recommend you use Array#select in your method
for example you might be able to just return this:
#articles.select { |art| art.ART_ARTICLE_NR.gsub(/[^0-9A-Za-z]/, '') == search }
assuming #articles is an array or collection you will always get an array back, even if it is 0, or 1 element
This answer would be a bit offtopic, but I would like to mention a splat operator:
[*val]
will produce array, consisting of either single val value whether it’s not an array, or the array itself whether val is an array:
▶ def array_or_single param
▷ [*param].reduce &:+ # HERE WE GO
▷ end
=> :array_or_single
▶ array_or_single [1,2,3]
=> 6
▶ array_or_single 5
=> 5
That said, you code would work with this tiny improvement:
- articles.each do |a|
+ [*articles].each do |a|
Hope it gives a hint on how one might handle the data, coming from the 3rd party. As an answer to your particular question, please follow the advises in the other answers here.
I'm trying to avoid an error message when pulling from a hash which may or may not have a value. I either want it to return the value or return nil.
I thought the try method would do it, but I'm still getting an error.
key not found: "en"
My hash is an hstore column called content... content['en'], etc.
content = {"es"=>"This is an amazing event!!!!!", "pl"=>"Gonna be crap!"}
Try method
#object.content.try(:fetch, 'en') # should return nil, but errors even with try method
I thought this would work but it doesn't. How else can I return a nil instead of an error?
Also, the content field itself might also be nil so calling content['en'] throws:
undefined method `content' for nil:NilClass
If you need to allow for object.content.nil?, then you'd use try. If you want to allow for a missing key then you don't want fetch (as Priti notes), you want the normal [] method. Combining the two yields:
object.content.try(:[], 'en')
Observe:
> h = { :a => :b }
=> {:a=>:b}
> h.try(:[], :a)
=> :b
> h.try(:[], :c)
=> nil
> h = nil
=> nil
> h.try(:[], :a)
=> nil
You could also use object.content.try(:fetch, 'en', nil) if :[] looks like it is mocking you.
See the Hash#fetch
Returns a value from the hash for the given key. If the key can’t be found, there are several options: With no other arguments, it will raise an KeyError exception; if default is given, then that will be returned; if the optional code block is specified, then that will be run and its result returned.
h = { "a" => 100, "b" => 200 }
h.fetch("z")
# ~> -:17:in `fetch': key not found: "z" (KeyError)
So use:
h = { "a" => 100, "b" => 200 }
h.fetch("z",nil)
# => nil
h.fetch("a",nil)
# => 100
Just use normal indexing:
content['en'] #=> nil
As of Ruby 2.0, using try on a possibly nil hash is not neat. You can use NilClass#to_h. And for returning nil when there is no key, that is exactly what [] is for, as opposed to what fetch is for.
#object.content.to_h["en"]
I have a Rails nested hash as follow:
class = [{"tutor" => {"id" => "Me"}}, {"tutor" => {}}]
I would like to extract id list, but the nested hash can be nil:
tutor_ids = class.map {|c| c['tutor']['id'].to_i }
In case the nested hash is nil, I'll get error.
How do I go about this?
First of all I think you were probably thinking of an array of hashes like so (given the same key was used multiple times:
klass = [{"tutor" => {"id" => "Me"}},{"tutor" => {}}]
Then you could map the tutor IDs with:
tutor_ids = klass.map {|k| k['tutor'] && k['tutor']['id'] }.compact
which would result in
=> ["Me"]
Compact will throw out all the nil values encountered afterwards.
id = class['tutor'] ? class['tutor']['id'] : nil
Lets say I have an Array of content_categories (content_categories = user.content_categories)
I now want to add every element belonging to a certain categorie to content_categories with the category as a key and the the content-item IDs as elements of a set
In PHP something like this is possible:
foreach ($content_categories as $key => $category) {
$contentsByCategoryIDArray = Category.getContents($category[id])
$content_categories[$key][$contentsByCategoryIDArray]
}
Is there an easy way in rails to do this?
Greets,
Nico
Your question isn't really a Rails question, it's a general Ruby programming question.
Your description isn't very clear, but from what I understand, you want to group IDs for common categories using a Hash. There are various other ways of doing this, but this is easy to understand::
ary = [
'cat1', {:id => 1},
'cat2', {:id => 2},
'cat1', {:id => 3}
]
hsh = {}
ary.each_slice(2) { |a|
key,category = a
hsh[key] ? hsh[key] << category[:id] : hsh[key] = [category[:id]]
}
hsh # => {"cat1"=>[1, 3], "cat2"=>[2]}
I'm using a simple Array with a category, followed by a simple hash representing some object instance, because it makes it easy to visualize. If you have a more complex object, replace the hash entries with those objects, and tweak how you access the ID in the ternary (?:) line.
Using Enumerable.inject():
hsh = ary.each_slice(2).inject({}) { |h,a|
key,category = a
h[key] ? h[key] << category[:id] : h[key] = [category[:id]]
h
}
hsh # => {"cat1"=>[1, 3], "cat2"=>[2]}
Enumerable.group_by() could probably shrink it even more, but my brain is fading.
I'd use Enumerable#inject
content_categories = content_categories_array.inject({}){ |memo, category| memo[category] = Category.get_contents(category); memo }
Hash[content_categories.map{|cat|
[cat, Category.get_contents(cat)]
}]
Not really the right answer, because you want IDs in your array, but I post it anyway, because it's nice and short, and you might actually get away with it:
content_categories.group_by(&:category)
content_categories.each do |k,v|
content_categories[k] = Category.getContents(v)
end
I suppose it's works
If i understand correctly, content_categories is an array of categories, which needs to be turned into a hash of categories, and their elements.
content_categories_array = content_categories
content_categories_hash = {}
content_categories_array.each do |category|
content_categories_hash[category] = Category.get_contents(category)
end
content_categories = content_categories_hash
That is the long version, which you can also write like
content_categories = {}.tap do |hash|
content_categories.each { |category| hash[category] = Category.get_contents(category) }
end
For this solution, content_categories must be a hash, not an array as you describe. Otherwise not sure where you're getting the key.
contents_by_categories = Hash[*content_categories.map{|k, v| [k, Category.getContents(v.id)]}]