2016-05-16 10 views
1

Ich muss ein Array von Objekten erstellen, um Daten in einem Diagramm anzuzeigen, das die Daten für die letzten 7 Tage zeigt. Zu bestimmten Zeiten wird der 7-Tage-Datensatz in der Datenbank fehlen und daher muss ich den Datensatz anzeigen und den Wert als 0 markieren, um 7 Objekte im Array zu haben.Iterate durch Datum für die letzten 7 Tage

Im Moment habe ich

[ 
    { 
    "date": "2016-05-14", 
    "amount": 6000 
    }, 
    { 
    "date": "2016-05-12", 
    "amount": 12000 
    } 
] 

Was ich will, ist

[ 
    { 
     "date": "2016-05-14", 
     "amount": 6000 
    }, 
    { 
     "date": "2016-05-13", 
     "amount": 0 
    }, 
    { 
     "date": "2016-05-12", 
     "amount": 12000 
    }, 
    { 
     "date": "2016-05-11", 
     "amount": 0 
    }, 
    { 
     "date": "2016-05-10", 
     "amount": 0 
    }, 
    { 
     "date": "2016-05-09", 
     "amount": 0 
    }, 
    { 
     "date": "2016-05-09", 
     "amount": 0 
    } 
] 
+0

Können Sie uns sagen, ist Datum ein Feld in der Tabelle? –

+0

@PraveshKhatri 'paid_on' ja das ist das Feld also im Grunde, da es Datensätze für diese Daten gibt, die Daten abgerufen werden. für die Tage ist es nicht ich muss den Standardwert –

Antwort

6
ts = JSON.parse '[ 
{ 
    "date": "2016-05-14", 
    "amount": 6000 
}, 
{ 
    "date": "2016-05-12", 
    "amount": 12000 
}]' # support ruby < 2.2 

((Date.today-6..Date.today).map do |d| 
    [d.iso8601, {'amount' => 0, 'date' => d.iso8601 }] 
end.to_h.merge ts.group_by { |e| e['date'] }).values.flatten 

Hier beginnen wir damit, einen Hash der angeforderten Tage zu erstellen, die auf Nullwerte abgebildet werden, und dann mit einem Hash der vorhandenen Werte zusammenzuführen. Letztere hat Vorrang:

[Date.today].map do |d| 
    [d.iso8601, {'amount' => 0, 'date' => d.iso8601 }] 
end.to_h 
#⇒ { '2016-05-16' => {'amount' => 0, 'date' => '2016-05-16' } } 

(in der realen Hash gibt es sieben Punkte.)

Enumerable#group_by die gleiche Struktur erzeugt:

[ {'amount' => 42, 'date' => Date.today } ].group_by { |e| e['date'] } 
#⇒ { '2016-05-16' => [{'amount' => 42, 'date' => '2016-05-16' }] } 

Als letzter Schritt, verschmelzen wir diese in das ehemalige und loswerden von Datumsschlüsseln, indem man values eines Ergebnisses nimmt und es abflacht.


Mit Hash#default_proc:

hsh = Hash.new do |h, k| 
    ts.detect do |e| 
    e['date'] == k.iso8601 
    end || { 'amount' => 0, 'date' => k.iso8601 } 
end 
(Date.today-6..Date.today).map { |d| hsh[d] } 
+0

Verdammt, das ist glatt :) –

+0

Könnten Sie bitte ein bisschen Erklärung, damit ich lernen kann –

+0

@HarshaMV Dropped ein wenig Erklärung. – mudasobwa

1

Ich hoffe, das hilft.

data = [ 
{ 
    "date": "2016-05-14", 
    "amount": 6000 
}, 
{ 
    "date": "2016-05-12", 
    "amount": 12000 
}] # Given data 
dates = data.map do |datum| datum[:date] end # Extract dates from data. 
today = Date.today # Get today. 
dates_list = 7.times.map do |index| (today + index).strftime("%F") end # Get 7days from today. 
dates_list.each do |date| 
    next if dates.include? date # if data already includes the date, such as "2016-05-14", pass. 
    data << {"date": date, "amount": 0} # if data does not include the date, append it with amount 0. 
end 
1

Dies wird die richtige Antwort:

require 'date' 
require 'pp' 

data = [ 
    { 
    "date": "2016-05-14", 
    "amount": 6000 
    }, 
    { 
    "date": "2016-05-12", 
    "amount": 12000 
    } 
] 

pp data 

today = DateTime.now 

7.times do |day| 
    current_day = (DateTime.now - day).strftime("%F") 
    next if data.find_index {|hash| hash[:date] == current_day } 
    data.push({ "date": current_day, "amount": 0 }) 
end 

pp data 

Dies wird die folgende Ausgabe:

[{:date=>"2016-05-14", :amount=>6000}, 
{:date=>"2016-05-12", :amount=>12000}, 
{:date=>"2016-05-15", :amount=>0}, 
{:date=>"2016-05-13", :amount=>0}, 
{:date=>"2016-05-11", :amount=>0}, 
{:date=>"2016-05-10", :amount=>0}, 
{:date=>"2016-05-09", :amount=>0}] 

Wenn Sie das Array nach dem Datum sortiert werden sollen, können Sie diese verwenden:

data.sort {|a,b| a[:date] <=> b[:date] }.reverse 

Dies wird thi produzieren s Ausgang:

[{:date=>"2016-05-15", :amount=>0}, 
{:date=>"2016-05-14", :amount=>6000}, 
{:date=>"2016-05-13", :amount=>0}, 
{:date=>"2016-05-12", :amount=>12000}, 
{:date=>"2016-05-11", :amount=>0}, 
{:date=>"2016-05-10", :amount=>0}, 
{:date=>"2016-05-09", :amount=>0}] 
2

Ein funktionaler Ansatz über Karte reduziert, ist wahrscheinlich einfachste und effizienteste:

timeseries = [ 
    { 
    "date": "2016-05-14", 
    "amount": 6000 
    }, 
    { 
    "date": "2016-05-12", 
    "amount": 12000 
    } 
] 

last_7_days  = 6.days.ago.to_date..Date.today 

# Transform array of hashes into a single hash with each key as a Date object. 
# Eg. { "2016-05-12" => 12000, "2016-05-14" => 6000 } 
counts_each_day = timeseries.reduce(Hash.new(0)) do |acc, item| 
    acc.merge(Date.parse(item[:date]) => item[:amount]) 
end 

# Merge the results with an empty count, if it doesn't exist in our transformed hash. 
filled_timeseries = last_7_days.map do |day| 
    { date: day.to_s, amount: counts_each_day[day] } 
end 

Ergebnis

[ 
    { :date => "2016-05-10", :amount => 0 }, 
    { :date => "2016-05-11", :amount => 0 }, 
    { :date => "2016-05-12", :amount => 12000 }, 
    { :date => "2016-05-13", :amount => 0 }, 
    { :date => "2016-05-14", :amount => 6000 }, 
    { :date => "2016-05-15", :amount => 0 }, 
    { :date => "2016-05-16", :amount => 0 } 
] 
+0

'' keine implizite Umwandlung von Datum in String' bin ich bekomme den folgenden Fehler –

+0

Auf welcher Zeile ist der Fehler? –

+0

'acc.merge (Date.parse (item [: date]) => item [: menge])' that line –

1

Hier ist ein weiterer Ansatz, den ich denke, ziemlich klar ist, und erhält das Startdatum von den Daten und nicht von Date.today. Es wird davon ausgegangen, dass in der Eingabe keine ungültigen Datumsangaben vorhanden sind und dass die Eingabe nicht leer ist.

Beachten Sie, dass Sie mit der Angabe "string":... etwas sagen, das gleiche wie string:..., aber in einer viel verwirrenderen Weise. Ich vermute, das ist aus JSON-Daten, aber es sollte in ein Ruby-Array von Hashes geparst werden. Ich habe das in meinem Code geändert.

#!/usr/bin/env ruby 

require 'date' 


def to_date(string) 
    Date.strptime(string, '%Y-%m-%d') 
end 


# Transform the input to a hash whose keys are date objects 
# and whose values are the original input records 
def transform_input_to_hash(input_records) 
    input_records.each_with_object({}) do |record, hsh| 
    key = to_date(record[:date]) 
    hsh[key] = record 
    end 
end 


def fill_in_missing_dates(input_records) 
    input_record_hash = transform_input_to_hash(input_records) 

    start_date = input_record_hash.keys.first 
    output_dates = ((start_date - 6)..start_date).to_a.reverse 

    output_dates.each_with_object([]) do |date, array| 
    array << if input_record_hash.keys.include?(date) 
     input_record_hash[date] 
    else 
     { date: date, amount: 0 } 
    end 
    end 
end 



INPUT_RECORDS = [ 
    { 
    date: "2016-05-14", 
    amount: 6000 
    }, 
    { 
    date: "2016-05-12", 
    amount: 12000 
    } 
] 



output_array = fill_in_missing_dates(INPUT_RECORDS) 
puts output_array 

Übrigens funktioniert das in Ruby ohne Rails. Außerdem wird der Code unter https://gist.github.com/keithrbennett/fd876aac938f1e5d6222896dbd30e8f2 veröffentlicht.

Verwandte Themen