2017-02-19 6 views
0

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

Прежде всего здесь некоторые из моего кода:

struct CalorieLog { 

    var date: Date 
    var calories: Int 

} 

    var logs: [CalorieLog] = [] 

    func logCalories() { 

    //... calculate calories to log 

    let currentDate: Date = Date() 
    let calories: Int = calculatedCalories 

    logs.append(CalorieLog(date: currentDate, calories: calculatedCalories)) 

} 

Теперь как я группа элементов CalorieLog в массиве журналов по дням и получить сумму всех калорий зарегистрированных в день? А может быть, сортировать их в ряд словарей? например Словарь (String: Int), чтобы (День: Всего Калорий)

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

ответ

1

Чтобы получить журналы, отсортированные по дате, вы можете просто сделать :

logs.sorted(by: { $0.date < $1.date })

чтобы получить словарь, который отображает день на сумму калорий в тот день, когда вы можете сделать это:

let dateFormatter = DateFormatter() 
dateFormatter.dateFormat = "dd MMM yyyy" 

var calorieCalendar = [String: Int]() 
for log in logs { 
    let date = dateFormatter.string(from: log.date) 
    if let _ = calorieCalendar[date] { 
     calorieCalendar[date]! += log.calories 
    } else { 
     calorieCalendar[date] = log.calories 
    } 
} 

для logs установка как этот

logs.append(CalorieLog(date: Date(), calories: 1)) 
logs.append(CalorieLog(date: Date.init(timeIntervalSinceNow: -10), calories: 2)) 
logs.append(CalorieLog(date: Date.init(timeIntervalSinceNow: -60*60*24*2), calories: 3)) 

Код выше будет производить словарь, как это:

["17 Feb 2017": 3, "19 Feb 2017": 3] 
1

Много того, что вы пытаетесь сделать, может быть выполнена с использованием map, sorted, filtered и reduce функции Свифта.

struct CalorieLog { 

    var date: Date 
    var calories: Int 

} 

var logs: [CalorieLog] = [] 

// I changed your method to pass in calculatedCalories, we can make that random just for learning purposes. See below 
func logCalories(calculatedCalories: Int) { 

    let currentDate: Date = Date() 

    logs.append(CalorieLog(date: currentDate, calories: calculatedCalories)) 

} 

// This is a method that will calculate dummy calorie data n times, and append it to your logs array 
func addDummyCalorieData(n: Int, maxRandomCalorie: Int) { 

    for _ in 1...n { 
     let random = Int(arc4random_uniform(UInt32(maxRandomCalorie))) 

     logCalories(calculatedCalories: random) 
    } 

} 

// Calculate 100 random CalorieLog's with a max calorie value of 1000 calories 
addDummyCalorieData(n: 100, maxRandomCalorie: 1000) 

// Print the unsorted CalorieLogs 
print("Unsorted Calorie Data: \(logs)") 

// Sort the logs from low to high based on the individual calories value. 
let sortedLowToHigh = logs.sorted { $0.calories < $1.calories } 

// Print to console window 
print("Sorted Low to High: \(sortedLowToHigh)") 

// Sort the CalorieLogs from high to low 
let sortedHighToLow = logs.sorted { $1.calories < $0.calories } 

// Print to console window 
print("Sorted High to Low: \(sortedHighToLow)") 

// Sum 
// This will reduce the CaloreLog's based on their calorie values, represented as a sum 
let sumOfCalories = logs.map { $0.calories }.reduce(0, +) 

// Print the sum 
print("Sum: \(sumOfCalories)") 

Если вы хотите, чтобы сопоставить CalorieLogs как массив словарей вы могли бы сделать что-то вроде этого:

let arrayOfDictionaries = logs.map { [$0.date : $0.calories] } 

Однако это своего рода неэффективна. Зачем вам нужен набор словарей? Если вы просто хотите отслеживать потребляемые/сжигаемые калории на определенную дату, вы можете просто сделать один словарь, где дата - ваш ключ, а массив Int - это значение, которое представляет все калории за этот день. Вы, вероятно, потребуется только один словарь, т.е.

var dictionary = [Date : [Int]]() 

Тогда вы могли бы найти все калории на дату, говоря dictionary[Date()]. Хотя имейте в виду, что вам придется иметь точную дату и время. Вы можете изменить ключ своего словаря на что-то вроде String, который просто представляет дату, такую ​​как 2/19/2017, что можно сравнить проще. Это необходимо учитывать при проектировании вашей модели.

+0

Спасибо за ваш ответ. Как я получу сумму калорий каждого дня, а не общее количество калорий? А затем иметь массив с днем ​​в качестве ключа и суммы этого дня в качестве значения? – He1nr1ch

+0

@ He1nr1ch - См. Ответ Мэтта ниже. Я думаю, что это поможет вам достичь того, что вы просите – Pierce

1

Многое зависит от того, что вы подразумеваете под «днем». Таким образом, в этом очень упрощенном примере я просто использую определение по умолчанию, то есть день, когда календарь определяет его для определенной даты (не учитывая реалии часового пояса).

Вот некоторые основные данные:

struct CalorieLog { 
    var date: Date 
    var calories: Int 
} 
var logs: [CalorieLog] = [] 
logs.append(CalorieLog(date:Date(), calories:150)) 
logs.append(CalorieLog(date:Date(), calories:140)) 
logs.append(CalorieLog(date:Date()+(60*60*24), calories:130)) 

Теперь мы строим словарь, где ключ является ordinality День на дату, а значение представляет собой массив из всех журналов, имеющих тот же день, как их дата:

var dict = [Int:[CalorieLog]]() 
for log in logs { 
    let d = log.date 
    let cal = Calendar(identifier: .gregorian) 
    if let ord = cal.ordinality(of: .day, in: .era, for: d) { 
     if dict[ord] == nil { 
      dict[ord] = [] 
     } 
     dict[ord]!.append(log) 
    } 
} 

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

for (ord,logs) in dict { 
    print(ord) 
    print(logs.reduce(0){$0 + $1.calories}) 
} 
+0

Приятное использование обыденности в '.era'! Обратите внимание, что мутация в месте значений массива ('dict [ord] !. append (log)') в 'dict' может быть убийцей производительности (без учета последствий преждевременной оптимизации ...) из-за временного совместного использования право собственности на массив (ы), запускающий copy-on-write, как описано [например, в этом Q & A] (http://stackoverflow.com/questions/41079687/). Использование remove-mutate-replace в кубе необязательного оператора 'if', связанного с необязательным, может быть использовано в качестве альтернативы, чтобы избежать этого (' var ords = dict.removeValue (forKey: ord) ?? [] '/' ords.append (log) '/' dict [ord] = ords'). – dfri

+0

... хотя в этом случае размер массивов должен быть небольшим (если только мы не имеем дело с действительно большим eater :) с элементами легкого веса, поэтому я не вижу, чтобы это когда-либо становилось проблемой. Тем не менее я оставляю комментарий выше, так как это может быть полезно для кого-то, кто наткнется на этот конкретный вопрос и применит его к приложению, в котором производительность может стать проблемой. – dfri

+1

@dfri Точка приветствуется; Я уже думал о замене этой строки мутатом и заменой. На самом деле я был удивлен, что такой вид приложения на месте работал вообще; Я бы ожидал, что 'dict [ord]' будет значением 'let'. – matt

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