0

I am trying to create a new array of objects from an existing array with objects within Swift 3. So I have an array of letters that contain objects that begin with that letter (the name key of that object) so that I can use that new array of dictionaries for the sections within a tableview. What I would like to have is that the first letters of each name key of each object will be examine, than extract the first letter of the name and store it as an array.

For example, a dictionary contains a name like "Lisa". The first letter is L and that one doesn't exists yet. It will create an array for the letter L. Check the next entry. If the next entry starts with an L again, add it to the array with the letter L. If the next one starts with an S, create a new array of S and add it to that array etc etc.

var names = [

    [
        "name" : "Lisa",
        "street" : "Lorem ipsum"
    ],
    [
        "name" : "Jeffrey",
        "street" : "Lorem ipsum"
    ],
    [
        "name" : "Arnold",
        "street" : "Lorem ipsum"
    ],
    [
        "name" : "Jacob",
        "street" : "Lorem ipsum"
    ],
    [
        "name" : "Sam",
        "street" : "Lorem ipsum"
    ],
    [
        "name" : "Anita",
        "street" : "Lorem ipsum"
    ],
    [
        "name" : "Lotte",
        "street" : "Lorem ipsum"
    ]

]


func createArrarDic(array: [[String: String]]) -> [[String: String]] {
    var result = [[String: String]]()

    // Loop through array
    for object in array {

        for (key, value) in object {

            let index = value.index(value.startIndex, offsetBy: 1)
            let firstLetter = value.substring(to: index).uppercased()
            print(firstLetter)

            if result[firstLetter] != nil {
                result[firstLetter]!.append(object)
            } else {
                result[firstLetter] = [object]
            }


        }
    }


    return result
}
createArrarDic(array: names)

I came up with above code, but it will throw me an error of: Cannot subscript a value of type '[[String : String]]' with an index of type 'String'

It clear that I do something wrong with the result declaration, but can't figure out how it should be. The check for the first letter should be done in the object, but to reorder and create a new array for each letter it should not be done within that object, but one level above it.

Also the problem is that for(key, value) will examine all the key/value- pairs in that object and I only need the "name" key.

Update

I have came to the following code. I have the return type and the return value to [String: [String: String]]() because of it should start with the letter that contains the object referred to it. The only problem now is that I don't know how to append an object to the array of a specific letter, because of .append or something like += operator doesn't work. Any thoughts on this one?

func createArrarDic(array: [[String: String]]) -> [String: [String: String]] {
    var result = [String: [String: String]]()

    // Loop through array
    for object in array {

        // Loop through dictionaries
        for (_, value) in object {

            let index = value.index(value.startIndex, offsetBy: 1)
            let firstLetter = value.substring(to: index).uppercased()
            print(firstLetter)

            if result[firstLetter] != nil {
                // Append
                result[firstLetter]! += object
            } else {
                result[firstLetter] = object
            }


        }
    }


    return result
}
let changedArray = createArrarDic(array: names)
halfer
  • 19,824
  • 17
  • 99
  • 186
Caspert
  • 4,271
  • 15
  • 59
  • 104
  • You should most probably be using an array of structs here, not an array of dictionaries. – Hamish Oct 26 '16 at 16:06
  • Take a look at [this Q&A](http://stackoverflow.com/questions/31220002/how-to-group-by-the-elements-of-an-array-in-swift) – if you used both the `categorise(_:)` method , and an array of structs, you could just say `names.categorise { String($0.name.characters.prefix(1)).uppercased() }` – Hamish Oct 26 '16 at 16:58

1 Answers1

1

I think this does what you want:

func createArrarDic(array: [[String: String]]) -> [String: [[String: String]]] {

  return array.reduce([String: [[String: String]]]()) { result, object in

    guard let name = object["name"] else {
      return result
    }

    if name.characters.count == 0 {
      return result
    }

    let firstLetter = String(name[name.startIndex]).uppercased()

    var mutableResult = result
    if mutableResult[firstLetter] == nil {
      mutableResult[firstLetter] = [object]
    } else {
      mutableResult[firstLetter]?.append(object)
    }

    return mutableResult
  }
}
ganzogo
  • 2,516
  • 24
  • 36
  • Ah, I updated my question right now, maybe you can help me with the last thing. The above code works for me, only it will now store the name properties and I would like to store the object in general. – Caspert Oct 26 '16 at 16:28
  • One more thing: you probably want a check like `if name.characters.count == 0 { return result }`, otherwise this will break when `"name"` is the empty string. – ganzogo Oct 26 '16 at 16:37
  • Where should that be placed in code? Around the return array.reduce or? And thanks for the great solution! – Caspert Oct 26 '16 at 16:39
  • No probs - have updated the answer to show you what I mean. – ganzogo Oct 26 '16 at 16:40