#arrays #swift
#массивы #swift
Вопрос:
У меня есть Country
объект и City
объект
struct Country: {
let name: String
let countryCode: String
let cities: [City]
let population: Int
init(name: String, countryCode: String, cities: [City], population: Int) {
self.name = name
self.countryCode = countryCode
self.cities = cities
self.population = population
}
}
struct City {
let id: Int
let name: String
let latitude: Double
let longitude: Double
let countryCode: String
let population: Int
}
Входящие JSON
данные выглядят следующим образом, которые декодируются в [City]
массив
{
"cities":[
{
"id":1,
"name":"Paris",
"latitude":0,
"logitude":0,
"country_code":"FR",
"population":0
},
{
"id":2,
"name":"Nice",
"latitude":0,
"logitude":0,
"country_code":"FR",
"population":0
},
{
"id":3,
"name":"Berlin",
"latitude":0,
"logitude":0,
"country_code":"DE",
"population":0
},
{
"id":4,
"name":"Munich",
"latitude":0,
"logitude":0,
"country_code":"DE",
"population":0
},
{
"id":5,
"name":"Amsterdam",
"latitude":0,
"logitude":0,
"country_code":"NL",
"population":0
},
{
"id":6,
"name":"Leiden",
"latitude":0,
"logitude":0,
"country_code":"NL",
"population":0
}
]
}
Как я мог бы эффективно создать [Country]
массив из [City]
массива? Я пытался использовать reduce:into:
, но не уверен, что это то, что я должен использовать.
Я знаю, что мог бы использовать пустой массив и добавлять / создавать страны одну за другой, затем искать, если она уже есть, и добавлять к ней город. Это создает ужасно выглядящий код, как для меня. Я чувствую, что есть элегантное решение этой проблемы с использованием функций map или reduce.
reduce:into:
код, который я пробовал до сих пор
func transformArrayOf(_ cities: [City]) -> [Country] {
let empty: [Country] = []
return cities.reduce(into: empty) { countries, city in
let existing = countries.filter { $0.countryCode == city.countryCode }.first
countries[existing].cities.append(city)
}
}
Редактировать:
Функция получает только [City]
массив. Поэтому страны должны создаваться только из этого.
Dictionary(grouping:by:)
с map(_:)
отлично работает! Две строки вместо вложенных for
циклов и if
операторов 🙂
И Country
имя может быть проанализировано из кода страны
Комментарии:
1.
JSON
Это деталь реализации. Если вы хотите, чтобы мы могли запускать эти фрагменты и могли лучше помогать, я предлагаю вам заменитьJSON
текст литералом массива, содержащим жестко запрограммированныеCity
экземпляры struct . Таким образом, нам не нужно разбираться в логике декодирования json только для запуска этого кода2. Как создаются ваши страны? Они содержат данные, которые не получены из городов (например
name
,countryName
(чем они отличаются?) И т. Д.
Ответ №1:
Используйте Dictionary(grouping:by:)
и map(_:)
комбинируйте, чтобы получить ожидаемый результат.
let countries = Dictionary(grouping: cities, by: { $0.countryCode }).map { (countryCode, cities) -> Country in
return Country(name: "", countryCode: countryCode, countryName: "", cities: cities, population: cities.reduce(0) { $0 $1.population })
}
Поскольку значения для name
и countryName
неизвестны, я использовал empty String
( ""
) для обоих.
Комментарии:
1. Создание экземпляра Country с пустыми значениями на самом деле не добавляет никакого значения, поэтому этот ответ выглядит для меня как дубликат. И ваше предположение, что все люди живут в городах, неверно.
2. @JoakimDanielson я не думаю, что это дубликат. А насчет пустых строк я уже упоминал, что они неизвестны и ОП может заполнить сам.
3. Нет, свойства
let
объявлены 🙂 Было бы лучше узнать из OP, откуда должны поступать данные по странам из IMO
Ответ №2:
Это то, что Dictionary(grouping:by:)
для:
let citiesByCountryCode = Dictionary(grouping: cities, by: .countryCode)
Но вам понадобится отдельная логика для создания стран, потому что они содержат данные, которые не являются производными от городов, таких как name
countryName
(чем они отличаются?) и т.д.