Как работать с Dictionary в CoreData?

dictionary
coredata

#1

Есть приложение с Core Data - в нем две сущности, допустим “Ученики” и “Предметы”. В первом перечислены параметры Имя, Возраст, Пол и т.д… Во втором перечислены Название предмета, описание предмета, план обучения и т.д… Мне надо связать Учеников с Предметами, при этом чтобы еще оценка сохранялась, получается типа “табеля оценок”, например:
"ученик Иванов:
1)математика - 5
2)история - 4
3)география - 5"
Тут наверняка надо использовать Dictionary. Но не могу дотукаться - где и как оценки прописать?
Я не могу найти данных по CoreData где бы обьяснялась похожая схема.


Сохранения в CoreData
#2

Сделайте три сущности ученики/оценки/предметы и свяжите учеников с оценками а оценки с предметами.


#3

Спасибо за ответ
Я попробовал, но не понял как тогда будет работать связь “Ученик - Предмет” ?
У ученика будут несколько оценок (возможно одинаковых), без "Предметов"
У “Предмета” будут несколько оценок (от разных учеников), но без прямой привязки к Ученикам


#4

Ученик -> Оценка -> Предмет, у ученика есть оценки у оценок предметы. Связи в CoreData двухсторонние (по крайней мере должны быть) по этому Предмет -> Оценка -> Ученик, то же будет работать. Можете сделать ещё одну связь Ученик -> Предмет (на случай если ученик не получил оценку по предмету) и в цикле предметов фильтровать оценки по предмету.


#5

Спасибо за ответ, но честно говоря не понятно как это практически в коде сделать. В интернете нахожу информацию про Core Data, но про связи сущности везде показывают только в виде графики, а не в программном коде. Вот ниже код из моего приложения. Есть сущность Heroes (это Ученики), куда заполняются его данные при нажатии кнопки Save. Я создал сущности Badges (это типа Предметы) и Status (Оценки). В Badges есть name. В Status есть badgeStatus. Графически я сделал между ними стрелочки и всё - дальше непонятно как это использовать. В примерах, которые я нагуглил в основном рассматривают связь только между 2 сущностями, в моем же случае это 3 сущности

@IBAction func saveButtonPressed(sender: AnyObject) {

    if nameHeroTextField.text == "" || ageHeroTextField.text == "" /*|| genderHeroTextField.text == ""*/ {
        print("не все поля заполнены")
    }else{
        if let context = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer.viewContext{
        let fetchRequest: NSFetchRequest<Heroes>=Heroes.fetchRequest()
            fetchRequest.predicate = NSPredicate(format: "name != nil")
            var records = 0
            do {
               let count = try context.count(for: fetchRequest)
               records = count
            } catch {
              print (error.localizedDescription)
            }
            let heroes = Heroes(context: context)
            
            heroes.name = nameHeroTextField.text
            heroes.age = ageHeroTextField.text
            heroes.numberHero = Int32(records+1)
                 
            do {
                try context.save()
                print("Все сохранилось!")
            } catch let error as NSError {
                print ("Не удалось сохранить данные\(error),\(error.userInfo)")
            }
        }
       performSegue(withIdentifier: "newHeroClose", sender: self)
    }
}


#6

Смотрите пример: https://yadi.sk/d/fG7AFIw53NmJua


#7

Большое спасибо, так подробно расписали, что понадобится время чтобы переварить, но кое-какие выводы сделал

  1. Придется переделать у себя модель, во-первых у вас больше связей, а во вторых у меня все Entity уже в модели были объявлены как класс. Судя по вашему примеру и ответу на прошлый вопрос - класс надо объявить через создания отдельного файла, чтобы внутри можно было создать сабкласс
  2. К каждому герою надо присвоить все Badge (их около 50 штук). в смысле HeroA = [badge01, badge02,badge03…badge48, badge49, badge50]
  3. Потом уже можно будет через status.badge и status.hero присвоить каждому герою статус за каждый предмет.
    Я только не пойму почему вы в примере привязываете оценку к предмету и ученику, а не наоборот. Получается вот оценка 3, её присваиваем к “Математике” и к ученику Иванову, это немного сбивает с толку, хотя наверное это уже не так важно. В моем приложении будет выбор ученика, потом вылезает список предметов, потом заходим в любой предмет и там через кнопку ставим определенный status. В этот момент думаю нужно определить только какой предмет выбран и какой ученик и потом через status.badge и status.hero сохранить соответствующий статус (оценку)

#8

Что бы знать у каких учеников по каким предметам оценки)


#9

еще вопрос - у меня у сущности Heroes на самом деле 5 аттрибутов
нужно ли все эти аттрибуты указать через @NSManaged ? или там нужно указывать только ключевые аттрибуты, которые нужны для связи с другими сущностями


#10

Нужно указать :sweet_potato:


#11

В вашем примере последние 2 строки, где используете filter и map - если честно тут у меня мозг просто ломается
Понимаю, что написано чтобы коротко было, но сложно для понимания как определять оценку предмета
Это можно как-то по-другому написать?

hero.badges?.forEach { badge in
let values = hero.statuses?.filter { $0.badge == badge }.map { $0.value } ?? []
print(" (badge.name!) (values)")
}


#12
let result = try! context.fetch(request)
for hero in result {
    print(hero.name!)
    for badge in hero.badges ?? [] {
        var values = [Int]()
        for status in hero.statuses ?? [] where status.badge == badge {
            values.append(status.value)
        }
        print(" \(badge.name!) \(values)")
    }
}

#13

Извиняюсь что реально туплю, хоть убейте, но так и не получается у меня. С помощью ваших подсказок я сделал такую же модель, привязал к ученику все предметы, теперь мне надо поставить “оценки” (status). Я решил изначально всем поставить status = 0, то есть никакой предмет еще не проверялся. Сделал в отдельной функции. Тут берется конкретный выбранный ученик (selectedHero), его указываю в status.hero, потом поочередно также в статус загружаю каждый badge и заодно присваиваю значение = 0, вот мой код
func setHeroStatus() {
let status = Status(context: context!)
status.hero = selectedHero
for badge in selectedHero.badgeHero! { // перебираем все значки
status.badge = b //привязываем значки к статусу
status.value = 0 // у всех значков должно быть статус = 0
}}
try! context?.save()

Потом смотрю что сохранилось, программа почему-то выборочно сохраняет в некоторые Badge, а в некоторые ничего не сохраняет, вот что выгружает (Romashka - это имя ученика). Что я делаю не так?
Romashka
BADGE 1 []
BADGE 5 []
BADGE 4 []
BADGE 3 [0]
BADGE 2 [0]


#14

Нужно делать новый Status для каждого Badge:

func setHeroStatus() {
    for badge in selectedHero.badgeHero! {
        let status = Status(context: context!)
        status.hero = selectedHero
        status.badge = b
        status.value = 0
    }
}

#15

Получилось! Еще раз спасибо -)
Только 2 строчки передвинул внутрь цикла и все заработало
Хотя не очень понял почему это повлияло.
Я понимал, что объявляю let status = Status(context: context!),
потом указываю конкретного героя status.hero = selectedHero
а значит далее в цикле все badge и statusValue присваиваются к этому герою.
Догадываюсь, что без эти двух команд в цикле программа наверное присваивала только первому значению.
Еще мне непонятно как расшифровать это
for badge in hero.badges ?? []
точнее последние символы = ?? []
Это ведь используется в основном при присваении значений


#16

Если hero.badges nil то пустой сет и цикл не выполняется :wink:

Это кто сказал?