2

I have an array of objects which I want to store it in dictionary based on student id. In my dictionary my key is of type String and value is an array of string. How do I append the values in the array?

My student array:

var studentArray = [Student(id: '1', subject: 'History'), Student(id: '2', subject: 'History'), Student(id:'1', subject: 'Maths')]

My final dictionary should be like:

{'1': ['History', 'Maths'], '2': ['History']}

My code:

var dictionary = [String, [String]]()
for student in studentArray {
   dictionary.updateValue(student.subject, forKey: student.id)
}

This gives me output as:

{'1': ['Maths'], '2': ['History']}

I tried: dictionary[student.id].append([student.subject]) but this gives me nil output.

How do I append the value to an array inside the dictionary?

tech_human
  • 6,592
  • 16
  • 65
  • 107

3 Answers3

4

Everyone here is overcomplicating this. There's a dedicated API for this task lol

struct Student {
    let id: Int
    let subject: String
}

let students = [
    Student(id: 1, subject: "History"),
    Student(id: 2, subject: "History"),
    Student(id: 1, subject: "Maths")
]

let subjectsByStudentID = Dictionary
    .init(grouping: students, by: { $0.id })
    .mapValues { students in students.map { $0.subject } }

print(subjectsByStudentID) // => [2: ["History"], 1: ["History", "Maths"]]
Alexander
  • 59,041
  • 12
  • 98
  • 151
2

EDIT: Thanks to Martin's comment. The snippet below is the the most succinct answer I can think of. I was initially coming at it from a wrong direction. and I was getting an error. See comments

struct Student { 
    let id: Int
    let subject : String
}

var studentArray = [Student(id: 1, subject: "History"), Student(id: 2, subject: "History"), Student(id:1, subject: "Maths")]

typealias Subject = String
var dict : [Int: [Subject]] = [:]

for student in studentArray {

    (dict[student.id, default: []]).append(student.subject)
}

print(dict)

Previous answers:

struct Student { 
    let id: Int
    let subject : String
}

var studentArray = [Student(id: 1, subject: "History"), Student(id: 2, subject: "History"), Student(id:1, subject: "Maths")]

typealias Subject = String
var dict : [Int: [Subject]] = [:]

for student in studentArray {
    var subjects = dict[student.id] ?? [String]()
    subjects.append(student.subject)
    dict[student.id] = subjects
}

print(dict)

Or you can do it this way:

struct Student { 
    let id: Int
    let subject : String
}

var studentArray = [Student(id: 1, subject: "History"), Student(id: 2, subject: "History"), Student(id:1, subject: "Maths")]

typealias Subject = String
var dict : [Int: [Subject]] = [:]

for student in studentArray {
    if let _ = dict[student.id]{
        dict[student.id]!.append(student.subject)
    }else{
        dict[student.id] = [student.subject]
    }
}

print(dict)

whichever you like

mfaani
  • 33,269
  • 19
  • 164
  • 293
  • his code doesn't compile. he's using `'`. And it's an integer. I just assumed myself. typealias is just for improved readability... – mfaani Nov 23 '18 at 20:23
  • Simpler: `dict[student.id, default: []].append(student.subject)` – compare https://stackoverflow.com/q/42486686/1187415. – Martin R Nov 23 '18 at 20:27
  • @MartinR Aha. That's what I was trying to do. I was doing `(dict[student.id] ?? [String]()).append(student.subject)` which didn't work. I was getting the following error: **cannot use mutating member on immutable value: `'??'` returns immutable value**. I had to change my approach – mfaani Nov 23 '18 at 20:29
  • 1
    `let dict = students.reduce(into: [:]) { $0[$1.id, default: []].append($1.subject) }` – Leo Dabus Nov 23 '18 at 21:23
0

Not tested but this should works:

var dictionary = [String: [String]]()

for student in studentArray {
    if dictionary[student.id] == nil {
        dictionary[student.id] = [String]()
    }
    dictionary[student.id]?.append(student.subject)
}
Vin Gazoil
  • 1,942
  • 2
  • 20
  • 24