0

So here you can see my method which fetches JSON. My problem is that I want my loop to go through every object. Not one object 10 times like it is now. I know that player["1"] is causing it to fetch first object repeatedly, it is just for the sake of the example. I need to get every player information. Could someone please fix this logic and little bit explain the situation.

var homeTeamPlayers: [MatchUp]? = []

let urlRequest = URLRequest(url: URL(string: "http://www.fibalivestats.com/data/586746/data.json")!)

func fetchHomeTeam() {

    let task = URLSession.shared.dataTask(with: urlRequest) { (data,response,error) in

        if error != nil {
            print(error!)
            return
        }

        homeTeamPlayers = [MatchUp]()

        do {
            let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String : AnyObject]

            if let teamsFromJSON = json["tm"] as? [String : AnyObject] {
                if let homeTeam = teamsFromJSON["1"] as? [String : AnyObject] {
                    if let player = homeTeam["pl"] as? [String : AnyObject] {
                        for _ in player {
                            let homeTeamPlayer = MatchUp()
                            if let firstPlayer = player["1"] as? [String : AnyObject] {
                                if let name = firstPlayer["name"] as? String {

                                    homeTeamPlayer.homeTeam_name = name

                                }
                            }

                            homeTeamPlayers?.append(homeTeamPlayer)
                        }
                    }
                }
            }    
        } catch let error {
            print(error)
        }   
    } 

    task.resume()
}

Here is the JSON I would like to fetch...

{

    tm: {
        1: {

            pl: {
                1: {

                    name: "R. Miniotas"

                },
                2: {

                    name: "T. Delininkaitis"

                },
                3: {

                    name: "V. Cizauskas"

                },
                4: {

                    name: "T. Klimavicius"

                },
                5: {

                    name: "V. Lipkevicius"

                },
                6: {

                    name: "M. LinkeviÄius"

                },
                7: {

                    name: "D. Seskus"

                },
                8: {

                    name: "T. Michnevicius"

                },
                9: {

                    name: "D. Gvezdauskas"

                },
                11: {

                    name: "A. Valeika"

                }
            }
1

3 Answers 3

3

You need to enumerate the dictionary using for (key, value) in syntax:

if let players = homeTeam["pl"] as? [String : Any] {
   for (_, value) in players {
       let homeTeamPlayer = MatchUp()
       if let currentPlayer = value as? [String : Any],
          let name = currentPlayer["name"] as? String {
          homeTeamPlayer.homeTeam_name = name
       }

       homeTeamPlayers?.append(homeTeamPlayer)
   }
}

However to avoid empty Matchup instances I'd recommend

if let players = homeTeam["pl"] as? [String : Any] {
  for (_, value) in players {
       if let currentPlayer = value as? [String : Any],
          let name = currentPlayer["name"] as? String {
             let homeTeamPlayer = MatchUp()
             homeTeamPlayer.homeTeam_name = name
             homeTeamPlayers?.append(homeTeamPlayer)
       }   
   }
}

Note: The JSON dictionary in Swift 3 is [String:Any] and why is the homeTeamPlayers array optional?

And finally – as always - .mutableContainers is completely meaningless in Swift.

Sign up to request clarification or add additional context in comments.

6 Comments

I was typing up the same thing. The only addition I would add is that the player variable should be renamed players.
Good answer, but for (key, value) in dictionary does not deliver the key/value pairs in any defined order. If that's important, see my answer.
To Ivalo, do you need to loop through your entries in 1, 2, 3 order, or is the order not important?
@DuncanC Yes, order is rather important.
If order is important then see my answer.
|
1

Your JSON data is a dictionary of dictionaries of dictionaries.

It looks like there is an outer key "tm" (teams) that then contains a dictionary with keys "1", "2", etc for each team, and then inside the teams, there are more dictionaries that again use the keys "1", "2", "3", etc for the players. This is not a great structure.

If you don't care about the order in which you fetch the items from your dictionaries then you can use the syntax:

for (key, value) in dictionary

...to loop through all the key/value pairs in a dictionary, but you do need to be aware that the order you get the key/value pairs is not defined. It might give you the entries in key order sometimes, and not in order other times.

If order is important then you would need to fetch the keys first, sort them, and then fetch the items (or some other technique). Getting the values sorted by key might look like this:

let keys = dictionary.keys.sorted($0 < $1)

for aKey in keys {
   let aValue = dictionary[aKey]
   //Do whatever you need to do with this entry
}

EDIT:

Your JSON data needed some editing to make it legal JSON:

{
  "tm" : {
    "1" : {
      "pl" : {
        "7" : {
          "name" : "D. Seskus"
        },
        "3" : {
          "name" : "V. Cizauskas"
        },
        "8" : {
          "name" : "T. Michnevicius"
        },
        "4" : {
          "name" : "T. Klimavicius"
        },
        "11" : {
          "name" : "A. Valeika"
        },
        "9" : {
          "name" : "D. Gvezdauskas"
        },
        "5" : {
          "name" : "V. Lipkevicius"
        },
        "1" : {
          "name" : "R. Miniotas"
        },
        "6" : {
          "name" : "M. LinkeviÃÂius"
        },
        "2" : {
          "name" : "T. Delininkaitis"
        }
      }
    }
  }
}

(All the keys and values had to be enclosed in quotes, and I needed to add closing braces to terminate the object graph)

I wrote code that took the above and read it into a JSON object, and then wrote it back out to JSON without whitespace. I then converted all the quotes to \".

Here is tested code that parses the JSON into objects, and then walks your data structure, with quite a bit of error checking:

let jsonString = "{\"tm\":{\"1\":{\"pl\":{\"7\":{\"name\":\"D. Seskus\"},\"3\":{\"name\":\"V. Cizauskas\"},\"8\":{\"name\":\"T. Michnevicius\"},\"4\":{\"name\":\"T. Klimavicius\"},\"11\":{\"name\":\"A. Valeika\"},\"9\":{\"name\":\"D. Gvezdauskas\"},\"5\":{\"name\":\"V. Lipkevicius\"},\"1\":{\"name\":\"R. Miniotas\"},\"6\":{\"name\":\"M. LinkeviÃius\"},\"2\":{\"name\":\"T. Delininkaitis\"}}}}}"
guard let data = jsonString.data(using: .utf8) else {
  fatalError("Unable to convert string to Data")
}

var jsonObjectOptional: Any? = nil
do {
  jsonObjectOptional = try JSONSerialization.jsonObject(with: data, options: [])
} catch {
  print("reading JSON failed with error \(error)")
}

guard let jsonObject = jsonObjectOptional as? [String: Any] else {
  fatalError("Unable to read JSON data")
}

//Fetch the value for the "tm" key in the outer dictionary
guard let teamsFromJSON = jsonObject["tm"] as? [String : Any] else {
  fatalError("Can't fetch dictionary from JSON[\"tm\"]")
}

let teamKeys = teamsFromJSON
  .keys                                                           //Fetch all the keys from the teamsFromJSON dictionary
  .sorted{$0.compare($1, options: .numeric) == .orderedAscending} //Sort the key strings in numeric order
print("teamsFromJSON = \(teamsFromJSON)")

//loop through the (sorted) team keys in teamKeys
for aTeamKey in teamKeys {
  guard let aTeam = teamsFromJSON[aTeamKey] as? [String: Any] else {
    print("Unable to read array of teams")
    continue
  }

  //Fetch the dictionary of players for this team
  guard let playersDict = aTeam["pl"] as? [String: Any] else {
    print("Unable to read players dictionary from team \(aTeamKey)")
    continue
  }

  //Fetch a sorted list of player keys
  let playerKeys = playersDict
    .keys
    .sorted{$0.compare($1, options: .numeric) == .orderedAscending}

  print("playerKeys = \(playerKeys)")

  //Loop through the sorted array of player keys
  for aPlayerKey in playerKeys {

    //Fetch the value for this player key
    guard let aPlayer = playersDict[aPlayerKey] as? [String: String] else {
      print("Unable to cast aPlayer to type [String: String]")
      continue
    }

    //Attempt to read the "name" entry for this player.
    guard let playerName = aPlayer["name"] else {
      continue
    }

    //Deal with a player in this team
    print("In team \(aTeamKey), player \(aPlayerKey) name = \(playerName)")
  }
}

if let jsonData = try? JSONSerialization.data(withJSONObject: jsonObject, options: []),
  let jsonString = String(data: jsonData, encoding: .ascii) {
  print("\n\njsonString = \n\(jsonString)")
}

The output of that code is:

playerKeys = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "11"]
In team 1, player 1 name = R. Miniotas
In team 1, player 2 name = T. Delininkaitis
In team 1, player 3 name = V. Cizauskas
In team 1, player 4 name = T. Klimavicius
In team 1, player 5 name = V. Lipkevicius
In team 1, player 6 name = M. LinkeviÃius
In team 1, player 7 name = D. Seskus
In team 1, player 8 name = T. Michnevicius
In team 1, player 9 name = D. Gvezdauskas
In team 1, player 11 name = A. Valeika

(Your data is missing an entry for player 10.)

3 Comments

Could you please write it out for me using my example data because I am struggling and do not really understand how to implement it.
If the keys "1", "2" "3", etc. are read in as Ints then the code would need to be changed slightly.
Thank you for your time! I really appreciate that you took the time to help me!
0

Suppose this is my URL:

URL: https://maps.googleapis.com/maps/api/directions/json?origin=19.0176147,72.8561644&destination=26.98228,75.77469&sensor=false

The URL mentioned above when entered in browser gives me a JSON response from google. In this response, I have to fetch the value of distance and duration.

So my swift code to fetch the value is:

do{

        let data = try Data(contentsOf: url!)

        let json = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: AnyObject]

        if let routes = json?["routes"] as AnyObject? as? [[String: AnyObject]] {

            //print(routes)

            for r in routes {

                if let legs = r["legs"] as? [[String: AnyObject]] {

                    for l in legs {

                        //Start Address
                        print("Start Address: \((l["start_address"]!) as Any)")

                        // End Address
                        print("End Address: \((l["end_address"]!) as Any)")

                        //Distance
                        if let distance = l["distance"] as? [String: AnyObject] {

                            distanceResult = (distance["text"]!) as Any as? String

                            print("Distance: \(distanceResult!)")

                        }

                        // Duration
                        if let duration = l["duration"] as? [String: AnyObject] {

                            durationResult = (duration["text"]!) as Any as? String

                            print("Duration: \(durationResult!)")
                        }
                    }

                    googleResult = distanceResult+durationResult

                }
            }
        }
    }
    catch
    {
        distanceResult = ""
        durationResult = ""
        print("error in JSONSerialization")
    }

    if(googleResult != ""){
        googleResult = "Distance: \(distanceResult!), Duration: \(durationResult!)"
    }
    else{
        print("googleResult is nil")
        distanceResult = ""
        durationResult = ""
    }

    return googleResult

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.