简体   繁体   中英

Combine 2 arrays of hashes into one JSON response

I'm working on a site that has a kind of newsfeed function. I want to pull data from a table with items and data from a table with users then combine parts of that data into a JSON response. Both are arrays of hashes. I don't though want to take all of those fields, only parts.

I was thinking I would pull both full records then do the combining in a method so as to only hit the DB twice. I can easily pull the last 5 items with:

class NewsfeedsController < ApplicationController
  before_action :authenticate_user!

  def index
    @newsfeed_data = json_response(return_items(params[:limit]))
  end

  private

  def return_items(limit)
    output = Item.last(limit=limit).reverse
  end
end

Now I'm stuck. I don't know how to remove the unwanted items from output or how to combine it with the user data. Item belongs to User in the model so there's a user_id field linking them.

Here are the schemas for Items and Users

create_table "items", force: :cascade do |t|
  t.string   "name"
  t.text     "description"
  t.string   "type"
  t.json     "properties"
  t.integer  "user_id"
  t.datetime "created_at",                   null: false
  t.datetime "updated_at",                   null: false
  t.index ["user_id"], name: "index_items_on_user_id", using: :btree
end

create_table "users", force: :cascade do |t|
  [lots of Devise settings...]
  t.string   "first_name"
  t.string   "last_name"
  t.string   "company"
  t.string   "position"
  t.string   "email"
  t.json     "tokens"
  t.datetime "created_at",                               null: false
  t.datetime "updated_at",                               null: false
  t.string   "avatar"
  [indexes...]
end

Here would be my desired output:

{
  "name": "Item name",
  "type": "Item type",
  "first_name": "User owner's first name",
  "company": "User's company",
  "avatar": "URL string to saved image"
}

The cleanest and most maintainable way to do this would be to use the ActiveRecordSerializers gem.

  1. Install the gem
  2. Create a file at app/serializers/newsfeeds_serializer.rb
  3. In that file, define a serializer that looks like this:

     class NewsfeedsSerializer < ActiveModel::Serializer attributes :name, :type, :first_name, :company, :avatar def first_name object.user.first_name end def company object.user.company end def avatar object.user.avatar end end 
  4. In your controller, write your index route as follows:

     class NewsfeedsController < ApplicationController before_action :authenticate_user! def index @items = Item .order(created_at: :desc) .limit(params[:limit]) .includes(:user) render json: @items, each_serializer: NewsfeedsSerializer end end 

This will serialize each Item object using the methods we defined in the serializer. Using :includes will preload the User items that each Item belongs to, so everything we'll use gets grabbed in one database call.

If you ever want to edit to the JSON you're sending, it's easy to make changes to the serializer. I suggest reading through the ActiveModelSerializer guides they provide if you run into any problems getting set up or you want more custom config ( camelCased keys or something like that).

You could do something like this but probably rails has something already built for extracting JSON responses.

def return_items(limit=10)
    items = Item.includes(:user).select('user_id, name, type, etc_you_want_to_pick').first(limit)
    response = []
    items.each do |item|
      response << {
        "name": item.name,
        "type": item.type,
        "first_name": item.user.first_name,
        "company": item.user.company,
        "avatar": item.user.avatar
      }
    end
    return response
  end

Probably if you are fine with having the whole user in the hash you could use the same AR query and then just call .as_json on the collection.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM