2015-05-10 4 views
0

У меня есть массив, содержащий хеши. Я бы хотел сортировать его по значению created_at. Вот пример структуры массива:Сортировка массива хэшей по значению

Примечание. Я написал человекочитаемые даты, значения будут отметкой времени.

[ 
    {"group1"=>[ 
        {:item1=>[{"name" => "Tim", "created_at"=>"4 weeks ago"}]}, 
        {:item2=>[{"name" => "Jim", "created_at"=>"3 weeks ago"}]}, 
        {:item3=>[{"name" => "Ted", "created_at"=>"2 weeks ago"}]}, 
      ] 
    }, 
    {"group2"=>[ 
       {:item1=>[{"name" => "Sally", "created_at"=>"1 month ago"}]}, 
       {:item2=>[{"name" => "Willa", "created_at"=>"2 months ago"}]}, 
       {:item3=>[{"name" => "Sammi", "created_at"=>"4 months ago"}]}, 
      ] 
    }, 
    {"group3"=>[ 
       {:item1=>[{"name" => "Jeff", "created_at"=>"1 month ago"}]}, 
       {:item2=>[{"name" => "Lois", "created_at"=>"1 day ago"}]}, 
       {:item3=>[{"name" => "Lisa", "created_at"=>"1 week ago"}]}, 
      ] 
    } 
] 

Я хотел бы организовать вышеуказанные данные таким образом, выходной сигнал будет group3 первым, так как она содержит item с created_at значением 1 день назад. Следующий будет group1, так как он содержит элемент со значением 2 недели назад, group2 будет последним, так как это самая последняя дата месяц назад.

Как я могу переупорядочить эти данные?

Я думал, я мог бы сделать что-то вроде

array_of_nested_hashes.each do |a| 
     a.sort_by { |k, v| v[:created_at] } 
end 

для сортировки данных в каждой группе по дате, а затем отсортировать каждую группу по дате его первый хэш - как это было бы самым последним хэш в каждой группе, давая мне полностью отсортированный хеш, который будет выглядеть так:

[ 
    {"group3"=>[ 
       {:item2=>[{"name" => "Lois", "created_at"=>"1 day ago"}]}, 
       {:item3=>[{"name" => "Lisa", "created_at"=>"1 week ago"}]}, 
       {:item1=>[{"name" => "Jeff", "created_at"=>"1 month ago"}]}, 
      ] 
    }, 
    {"group1"=>[ 
        {:item3=>[{"name" => "Ted", "created_at"=>"2 weeks ago"}]}, 
        {:item2=>[{"name" => "Jim", "created_at"=>"3 weeks ago"}]}, 
        {:item1=>[{"name" => "Tim", "created_at"=>"4 weeks ago"}]}, 
      ] 
    }, 
    {"group2"=>[ 
       {:item1=>[{"name" => "Sally", "created_at"=>"1 month ago"}]}, 
       {:item2=>[{"name" => "Willa", "created_at"=>"2 months ago"}]}, 
       {:item3=>[{"name" => "Sammi", "created_at"=>"4 months ago"}]}, 
      ] 
    }, 
] 
+0

Можете ли вы показать нам, что желаемый результат выглядит как? Также отметки времени являются точными, иначе сортировка будет выглядеть как целое, а затем следующая буква. т.е. 1 час назад> 1 день назад, хотя ваш пример выглядит нормально. – Anthony

+0

В этом случае желаемый вывод является хешем, переупорядоченным. Как бы то ни было, вы ответили на мой вопрос раньше - это тот же хэш, с которым я работаю, как и раньше, - кстати. – LpLrich

+0

Спасибо, в выводе для group_3 почему 1 месяц выше 1 дня? – Anthony

ответ

2

Вот моя попытка. Worflow является:

1) Сортировать все внутренние массивы, чтобы получить наибольшее значение (значение самого последнего числовой метку времени) к первому индексу.

2) С наибольшим значением в известном положении (индекс 0) во внутреннем массиве сортируйте внешние массивы в соответствии со значением первого индекса в своем внутреннем массиве.

# Part 1 
outer_list.map! do |h| 
    Hash[h.map do |k, v| 
     v = v.sort_by do |hsh| 
      hsh.first[1][0]['created_at'].to_i 
     end.reverse! 
     [k, v] 
    end] 
end 

# Part 2 
sorted = outer_list.sort_by do |h| 
    h.first[1][0].first[1][0]['created_at'].to_i 
end.reverse! 
+0

Я думал об этом, спасибо за ваше предложение, я попробую. По внешнему виду это то, что мне нужно. Большое спасибо – LpLrich

+0

Это не сработает, потому что 1 месяц больше 3 недель –

+1

@MohammadAbuShady - в своем вопросе он упоминает, что 'created_at' на самом деле является числовой меткой времени, а не строкой. –

0

После того, зная, что они на самом деле временные метки ..

вот мой ответ

obj = {that huge array} 
sorted_obj = obj.sort_by do |groups| 
    groups.values.map do |items| 
    items.map do |item| 
     item.values.flatten.first['created_at'] 
    end.max 
    end 
end 
+0

Спасибо за ответ, очень ценный, к сожалению, он не дает правильного вывода, поскольку более поздние значения удаляются из хэши «item», поэтому есть только самые последние. Мне нужны все значения, даже если фактический порядок конечного содержимого «элементов» не важен. – LpLrich

1

Edit:

Вот ответ для правильной интерпретации вопроса:

arr = [ 
    {"g1"=>[{i1: [{"ca"=>-28}]}, {i2: [{"ca"=>-21}]}, {i3: [{"ca"=>-14} ]}]}, 
    {"g2"=>[{i1: [{"ca"=>-30}]}, {i2: [{"ca"=>-60}]}, {i3: [{"ca"=>-120}]}]}, 
    {"g3"=>[{i1: [{"ca"=>-30}]}, {i2: [{"ca"=>-1}]}, {i3: [{"ca"=>-7} ]}]} 
] 

arr.sort_by { |h| h.first.last.map { |g| g["ca"] }.max }.reverse 
    #=> [{"g3"=>...}, {"g1"=>...}, {"g2"=>...}] 

Приведенное ниже объяснение также относится к этому вопросу.

Tide

Это один из способов вы можете сделать это, позволяя arr обозначать массив хэшей, которые вы хотите отсортировать.

Код

PER_SIZE = { 'day'=>1, 'week'=>7, 'month'=>30 } 

arr.sort_by do |g| 
    g.first.last.map do |h| 
    n, period = h.first.last.first["created_at"].scan(/(\d+) ([a-rt-z]+)/).first 
    n.to_i * PER_SIZE[period] 
    end.min 
end 
    #=>[{"group3"=>[{:item2=>[{"name"=>"Lois", "created_at"=>"1 day ago"}]}, 
    #    {:item3=>[{"name"=>"Lisa", "created_at"=>"1 week ago"}]}, 
    #    {:item1=>[{"name"=>"Jeff", "created_at"=>"1 month ago"}]}]}, 
    # {"group1"=>[{:item3=>[{"name"=>"Ted", "created_at"=>"2 weeks ago"}]}, 
    #    {:item2=>[{"name"=>"Jim", "created_at"=>"3 weeks ago"}]}, 
    #    {:item1=>[{"name"=>"Tim", "created_at"=>"4 weeks ago"}]}]}, 
    # {"group2"=>[{:item1=>[{"name"=>"Sally", "created_at"=>"1 month ago"}]}, 
    #    {:item2=>[{"name"=>"Willa", "created_at"=>"2 months ago"}]}, 
    #    {:item3=>[{"name"=>"Sammi", "created_at"=>"4 months ago"}]}]}] 

Объяснение

Сортировка может быть сделано путем преобразования каждой строки даты для числа дней. Начнем с назначения переменной счетчику arr.sort_by.Затем мы можем использовать Enumerator#next, чтобы получить каждое значение перечислителя, которое мы затем перейдем к блоку.

enum = arr.sort_by 
    #=> #<Enumerator: 
    #  [{"group1"=>[{:item1=> 
    #  [{"name"=>"Tim", "created_at"=>"4 weeks ago"}]},... 
    # :sort_by> 

Теперь присвоить первое значение счетчику к переменному блоку:

g = enum.next 
    #=> {"group1"=>[{:item1=>[{"name"=>"Tim", "created_at"=>"4 weeks ago"}]}, 
    #    {:item2=>[{"name"=>"Jim", "created_at"=>"3 weeks ago"}]}, 
    #    {:item3=>[{"name"=>"Ted", "created_at"=>"2 weeks ago"}]}]} 
arr1 = g.first.last 
    #=> ["group1", [{:item1=>[{"name"=>"Tim", "created_at"=>"4 weeks ago"}]}, 
    #    {:item2=>[{"name"=>"Jim", "created_at"=>"3 weeks ago"}]}, 
    #    {:item3=>[{"name"=>"Ted", "created_at"=>"2 weeks ago"}]}]] 
arr1 
    #=> [{:item1=>[{"name"=>"Tim", "created_at"=>"4 weeks ago"}]}, 
    # {:item2=>[{"name"=>"Jim", "created_at"=>"3 weeks ago"}]}, 
    # {:item3=>[{"name"=>"Ted", "created_at"=>"2 weeks ago"}]}] 

map проходит первый элемент arr к блоку, назначив его переменный блок:

h = {:item1=>[{"name"=>"Tim", "created_at"=>"4 weeks ago"}]} 

arr2 = h.first.last 
    #=> [{"name"=>"Tim", "created_at"=>"4 weeks ago"}] 

s = arr2.first["created_at"] 
    #=> "4 weeks ago" 
arr3 = s.scan(/(\d+) ([a-rt-z]+)/) 
    #=> [["4", "week"]] 
n, period = arr3.first 
    #=> ["4", "week"] 
n  #=> "4" 
period #=> "week" 
n.to_i * PER_SIZE[period] 
    #=> 4 * PER_SIZE['week'] 
    #=> 4 * 7 => 28 

Аналогично, второй и третий элементы arr1 сопоставляются с 21 и 14 (дней), соответственно. Затем мы вычисляем:

[28, 21, 14].min 
    #=> 14 

которая является значением sort_by использует для arr[0]. Аналогичным образом, sort_by значения arr[1] являются:

[30, 60, 120].min 
    #=> 30 

и arr[2] являются:

[30, 1, 7].min 
    #=> 1 

Поэтому arr сортируется на:

[arr[3], arr[1], arr[2]] 
+0

btw Я нашел этот камень, который должен сделать ваш подход более простым https://github.com/mojombo/chronic –

+0

Спасибо, @MohammadAbuShady. Я ожидаю, что это будет очень полезно для тех, кто пишет приложения с датой, выраженной по-разному. –

+0

Это отличный ответ для тех, кто хочет преобразовать строки в даты, я уверен, что другие найдут это очень полезным. Это выглядит хорошо, комментарии и объяснения очень полезны. Я уверен, что это будет большой помощью для людей. – LpLrich

Смежные вопросы