•  
  • 1
  •  
0

Группировка хешей в руби

Допустим, есть такой хеш:

h={
2010-03-01=>2
2010-03-02=>4
2010-03-03=>1
..
..
рабочий_день=>кол_во_часов
}

где каждый ключ представляет собой дату, а значение - целое число (кол-во проработанных часов в этот день). Как трансформировать этот хеш в новый, чтобы ключи хеша были неделями/месяцами/годами, а значения - суммами проработанных часов в этом периоде?

Пример того, как это должно выглядеть:

Недели:

h_weeks={7=>36, 8=>42, 9=>34 ..,..,n}

Если месяцы:

h_months={1=>170, 2=>180, 3=>146}

Если годы:

h_years={2009=>950, 2010=>446}

Спасибо

ruby.
спросил 669 дней назад Аватор пользователя Valentin Valentin
229 6
изменил 668 дней назад Аватор пользователя tc tc
718 15
300

2 ответа:

    •  
    • 1
    •  
    
    h = {
      Date.parse('2010-04-02') => 3,
      Date.parse('2010-05-03') => 5,
      Date.parse('2009-04-03') => 6
    }
    by_year = Hash.new(0)
    by_month = Hash.new(0)
    by_week = Hash.new(0)
    h.each{|d,v|
      by_year[d.year] += v
      by_month[d.month] += v
      by_week[d.cweek] += v
    }
    

    или более экзотический вариант:

    
    aggregates = {}
    h.each{|d,v|
      [:year, :month, :cweek].each{|period|
        aggregates[period] ||= Hash.new(0)
        aggregates[period][d.send(period)] += v
      }
    }
    p aggregates
    #=>{:year=>{2010=>8, 2009=>6}, :month=>{4=>9, 5=>5}, :cweek=>{13=>3, 18=>5, 14=>6}}
    
    

    Я просто не знаком с ruby, вы не против?

    ответил 668 дней назад Аватор пользователя tc tc
    718 15
    •  
    • 0
    •  

    Хардкорные однострочники на Ruby:

    #Для недель
    weeks = days.inject({}) { |w, d| w[d.first.cweek].nil? ? w[d.first.cweek] = d.last : w[d.first.cweek] = w[d.first.cweek] + d.last; w }
    #По годам
    years = days.inject({}) { |y, d| y[d.first.year].nil? ? y[d.first.year] = d.last : y[d.first.year] = y[d.first.year] + d.last; y }
    

    Приличный метод для сбора статистики (полный код, тестировал только на Ruby 1.9):

    require "date"
    require "pp"
    
    days = {
                Date.parse("2009-04-12") => 10,
                Date.parse("2010-04-12") => 20,
                Date.parse("2010-04-13") => 6,
                Date.parse("2010-04-14") => 1,
                Date.parse("2010-04-15") => 18,
                Date.parse("2010-04-16") => 0,
                Date.parse("2010-04-17") => 10,
                Date.parse("2010-04-18") => 2,
                Date.parse("2010-04-19") => 6,
                Date.parse("2010-04-20") => 3
            }
    
    def collect_stats data
        data.inject({}) do |stats, data|
            #Key of array is obviously a date object
            next unless data.first.kind_of? Date
            #Initializes year stats storage
            stats[data.first.year] = {:overall => 0, :weeks => {}} if stats[data.first.year] == nil
            #Appends stats regarding year (it will be filled under the 'overall' key)
            stats[data.first.year][:overall] += data.last
            #Appends stats regarding weeks
            stats[data.first.year][:weeks][data.first.cweek] += data.last rescue stats[data.first.year][:weeks][data.first.cweek] = data.last
            #Returns all data back to the user
            stats
        end
    end
    
    pp collect_stats days
    #=> {2009=>{:overall=>10, :weeks=>{15=>10}},
    #=> 2010=>{:overall=>66, :weeks=>{15=>57, 16=>9}}}
    
    ответил 664 дня назад Аватор пользователя Din Din
    50 1 1
Чтобы написать ответ, вы должны авторизироваться.