Ruby on Rails Active Record Query (.each, .collect, .map ...?) - ruby-on-rails

This is one example of one entry in my database:
Market id: 1, name: "Independence Park (Independently Run Farmers Market...", address: "3945 N. Springfield Ave., Chicago, IL", zipcode: "60618", created_at: "2013-01-01 21:22:24", updated_at: "2013-01-01 21:22:24"
All I want to do is list the 43 zipcodes from all the entries in my database. Why don't these queries work?
Market.all.each { |m| m.zipcode }
Market.all.zipcode
m = Market.all
m.each{ |m| m.zipcode }
Thanks!

If all you want is an array of zip codes, I would suggest to try this:
Market.pluck(:zipcode)

The other answers have pointed out the correct way to do what you want, but haven't explained why your other attempts didn't work (which is technically the question you asked).
The reason attempts 1 and 3 don't work is that each doesn't return anything, it's only use is to loop through a set of records and perform an action on them (such as updating them or using some data to call an external service). Replacing each with map would fix them, as map uses the return value of the block to return an array of values. This has a disadvantage in that map is an Array method, so all of your records will have to be loaded into memory before the values can be found.
Attempt 2 doesn't work as you're trying to call a field name on an ActiveRecord::Relation (all), so you'll end up raising a NoMethodError.
The neatest solution, and one that has already been pointed out, is to use pluck, which not only returns all the values for the requested fields, but does so at the database query level, so you call should be more efficient.

You could also do the following, it returns an array of zipcodes:
Market.all.map(&:zipcode)
Use Benchmark to determine which is better.

Related

Why does splitting a string return a (bad) value across multiple lines in my Rails API application, but not the console?

I have a GrapeSwaggerRails API application that takes a two dates and a comma-delimited string of category IDs. It should query the database for Records with a created_at within the two given dates and a category_id that matches one of the IDs passed to it. I'm not having any trouble with the dates, so I'll skip that for now. But let's say I want Records with categories matching 8, 2, or 1. In the code, it looks like "8,2,1". In the URL, it gets appended as &categories=%228%2C2%2C1%22.
Anyway, I figured one decent way of getting this to do what I want would be to convert that string into an array of integers like this: categories = params[:categories].split(',').map(&:to_i)
But given "8,2,1", the output is this (ignore the comment):
0 # <-- ?????
2
1
Very strange. In the definition of the API, params[:categories] looks like this: "8,2,1". But params[:categories].split(',') becomes the following:
"8
2
1"
That's a bit odd, isn't it? Running the map method on that turns it into that nonsense higher up, converting the "8 to a 0 for reasons I'm hoping to find out here. I know I could probably come at this problem from a different angle and sidestep the issue, but I'd rather try to get to the root of what's going wrong, so I can learn something from it. For reference, here's what the Rails console does when I put (as far as I can tell) the same data into it:
>> "8,2,1".split(',')
#=> ["8", "2", "1"]
map then works as expected.
>> "8,2,1".split(',').map(&:to_i)
#=> [8, 2, 1]
So my question is twofold. What's going wrong with this split function? Why does it behave differently in the console?
Because params[:categories] is actually
'"8,2,1"' # <- the outer ''s are just for illustration of a string.
If you pass &categories=8%2C2%2C1 it should work as expected.

Equivalent query replace where to find_by

Home.where(code: "x123").where.not(state_id: 1)
How can I use a equivalent query but instead of using "where" I want to use find_by.
find_by returns a single record (first matching the specified conditions), so there is no way (and sense) to do what you want.
EDIT
sure! But I want only one object!
The following query would do it then (it will search for a code 123 and any state_id except for 1. And it returns a first record matching this condition):
Home.find_by(code: '123', state_id: State.ids - [1])
If the goal is to return a single object you can simply use first:
Home.where(code: "x123").where.not(state_id: 1).first
If the reason you want to use find_by is because you want missing records to raise ActiveRecord::RecordNotFound then you can use first! instead, which will do exactly that.

Select with count distinct in ActiveRecord query not returning aggregated fields

I'm doing a select with a count-distinct in ActiveRecord, but it's not returning any of my aggregated fields.
User.
select(
'users.id, count(distinct(shc.id)) as shipping_credit_count,
count(distinct(sc.id)) as service_credit_count'
).
...
...
group('users.id')
Is only returning #<ActiveRecord::Relation [#<User id: 119>]> I was expecting to see the count in my aggregated fields? Why is nothing being returned?
Your query probably works as expected but the inspect method is throwing you of. Read my answer here for a better description: Why group calculation fields do not show up in query result?
You should be able to call service_credit_count and service_credit_count on your objects even though it does not show up when you log them.
I would however implement it a little bit different. I would on the User model add the methods
def service_credit_count
return service_credit_count_sql if self.respond_to?(:service_credit_count_sql)
services.count
end
def shipping_credit_count
return shipping_credit_count_sql if self.respond_to?(:shipping_credit_count_sql)
shippings.count
end
And then in your query name the fields with the suffix. This way you can always use these counts. There is also a small (quite imature) gem I have written that does this: https://github.com/trialbee/association_count

Is it possible to return all Key-Value Pairs from an object?

I currently have an object which has a number of dynamically created attributes/keys, which looks like the following:
#<Employee _id: 54af214d6175720da8130000, name: "Barry", company: ["Google", "Microsoft", "HP"], customFieldName: "ABC">
In order to fulfill my project's requirements, I need to be able to iterate through each element of this object, and print the Key:Value pairs. i.e.
Name: Barry
Company:
1. Google
2. Microsoft
3. HP
customFieldName: ABC
I've had a look around online (and found an article on reflection (which is what I would do in C#), which doesn't appear to be what I'm looking for. :/), but I can't work out how to do this.
A simple #employeeObj.each loop doesn't help, either. :/
Any help would be greatly appreciated,
Thanks heaps in advance!
You should just be able to call #employee.attributes which will return a hash and then you can loop over the key values as so:
#employee.attributes.each do |key, value|
end

Rails and Arel's where function: Can I call where on objects instead of making a call to the database?

Consider the following:
budget has many objects in its another_collection.
obj has many objects of the same type as object in its another_collection.
budget and some_collection are already declared before the following loop
they've been previous saved in the database and have primary keys set.
some_collection is a collections of objs.
-
some_collection.each do |obj|
another_obj = obj.another_collection.build
budget.another_collection << another_obj
end
budget.another_collection.collect {|another_obj| another_obj.another_obj_id}
=> [1, 2, 3, 4]
some_obj_with_pk_1 = some_collection.where(obj_id: obj.id)
some_obj_with_pl_1.id
=> 1
budget.another_collection.where(another_obj_id: some_obj_with_pk_1.id)
=> []
This shouldn't happen. What is happening is that rails queries the database for any items in another_collection with another_obj_id = 1. Since this collection hasn't been saved to the database yet, none of these items are showing up in the results.
Is there a function or something I can pass to Arel's where method that says to use local results only? I know I can just iterate over the items and find it but it would be great if I didn't have to do that and just use a method that does this already.
You could always use Enumeable#select which takes a block and returns only the elements that the block returns true for. You'd want to make sure that you had ActiveRecord retrieve the result set first (by calling to_a on your query).
records = Model.where(some_attribute: value).to_a
filtered_records = records.select { |record| record.something? }
Depending on your result set and your needs, it is possible that a second database query would be faster, as your SQL store is better suited to do these comparisons than Ruby. But if your records have yet to be saved, you would need to do something like the above, since the records aren't persisted yet

Resources