0

I'm mixing 2 arrays and want to sort them by their created_at attribute:

@current_user_statuses = current_user.statuses
@friends_statuses = current_user.friends.collect { |f| f.statuses }
@statuses = @current_user_statuses + @friends_statuses
@statuses.flatten!.sort!{ |a,b| b.created_at <=> a.created_at }

The @current_user_statuses and @friends_statuses each sort correctly, but combined they sort incorrectly, with the @friends_statuses always showing up on top sorted by their created_at attribute and the @current_user_statuses on the bottom sorted by their created_at attribute.

This is the view:

<% @statuses.each do |d| %>
<%= d.content %>
<% end %>
6
  • I'm sorry, but I'm really struggling to understand what the problem is. Would you mind rephrasing the text after the first code sample? Commented Jul 2, 2011 at 14:35
  • 2
    "the friends statuses always showing up on the top sorted by there created at attribute and the current_user_statuses in the bottem sorted by there created at attribute" --- and the times in created_at suggest that the records should be intermingled? Commented Jul 2, 2011 at 14:38
  • @gal, please show the desired result. Thanks. Commented Jul 2, 2011 at 14:42
  • 2
    Post an example output, so we can see the dates. Have you checked to make sure they're not just coincidentally ordered this way? Commented Jul 2, 2011 at 15:09
  • Unless you show a good sample of the data being tested, we can't help you further. Telling everyone who has answered "the sorting is not my problem... my friends statuses always showing up in the top" does not help. Take the time to provide a full data sample. Commented Jul 3, 2011 at 17:17

5 Answers 5

3

Try:

(current_user.statuses + current_user.friends.collect(&:statuses)) \
  .flatten.compact.sort_by(&:created_at)
Sign up to request clarification or add additional context in comments.

1 Comment

the sorting is not my problem... my friends statuses always showing up in the top...
1

You can not daisy chain the flatten! method like that. flatten! returns nil if no changes were made to the array. When you sort nil nothing will happen.

You need to separate them:

@statuses.flatten!
@statuses.sort! { ... }

1 Comment

Quite true, though this isn't really an answer to the question ;) When you sort nil, you'll get NoMethodError
0

Here's how I'd do it:

Set up the classes:

class User

  class Status
    attr_reader :statuses, :created_at
    def initialize(stats)
      @statuses = stats
      @created_at = Time.now
    end
  end

  attr_reader :statuses, :friends
  def initialize(stats=[], friends=[])
    @statuses = Status.new(stats)
    @friends = friends
  end
end

Define some instances, with some time gaps just for fun:

friend2 = User.new(%w[yellow 2])
sleep 1
friend1 = User.new(%w[orange 1])
sleep 2
current_user = User.new(%w[green 1], [friend1, friend2])

Here's how I'd do it differently; Get the statuses in created_at order:

statuses = [
  current_user.statuses, 
  current_user.friends.collect(&:statuses)
].flatten.sort_by(&:created_at)

Which looks like:

require 'pp'
pp statuses

# >> [#<User::Status:0x0000010086bd60
# >>   @created_at=2011-07-02 10:49:49 -0700,
# >>   @statuses=["yellow", "2"]>,
# >>  #<User::Status:0x0000010086bc48
# >>   @created_at=2011-07-02 10:49:50 -0700,
# >>   @statuses=["orange", "1"]>,
# >>  #<User::Status:0x0000010086bb30
# >>   @created_at=2011-07-02 10:49:52 -0700,
# >>   @statuses=["green", "1"]>]

I'm just building a temporary containing array to hold the current_user's status, plus the status of all the friends, then flattening it.

The (&:statuses) and (&:created_at) parameters are Rails short-hand for the statuses method of the instance, or created_at method of the instance.

2 Comments

the sorting is not my problem... my friends statuses always showing up in the top...
Actually, sorting is a problem in your code. You're using sort instead of sort_by which is significantly slower when dealing with data structures or objects.
0
@statuses = (@current_user_statuses + @friends_statuses).sort_by(&:created_at)

2 Comments

the sorting is not my problem... my friends statuses always showing up in the top...
Please prove that by providing the output of p statuses.map(&:created_at)
0

I know there are several solutions posted for your question. But all of these solutions can kill your system when the number of statuses grow in size. For this dataset, you have to perform the sorting and pagination in the database layer and NOT in the Ruby layer

Approach 1: Simple and concise

Status.find_all_by_user_id([id, friend_ids].compact, :order => :created_at)

Approach 2: Long and efficient

class User    
  def all_statuses
    @all_statuses ||=Status.all( :joins => "JOIN (
        SELECT friend_id AS user_id 
        FROM   friendships 
        WHERE  user_id = #{self.id}
      ) AS friends ON statuses.user_id = friends.user_id OR 
                      statuses.user_id = {self.id}",
      :order => :created_at
    )
  end    
end

Now you can get the sorted statuses in single query:

user.all_statuses

PPS: If this is my code I would further optimize the SQL. Refer to this answer for some more details.

2 Comments

the sorting is not my problem... my friends statuses always showing up in the top...
If you use the method I have suggested, this can only happen ONLY if the friend's statuses are newer than your statuses.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.