Swift фильтрует другие массивы на основе другого массива Bool

#arrays #swift #filter #flatmap

#массивы #swift #Фильтр #flatmap

Вопрос:

У меня есть массив Bools, и я хочу отредактировать массив оценок и массив дат для значений, которые являются False. Я не могу добраться до него. Я думал о получении элементов, которые являются ложными, и использовании этого массива для удаления этих элементов из массива score, но я могу представить, что есть прямой способ сделать это.

 let hbiCompleteArray = [true, true, true, true, false, true, true, false, false]

let hbiScoreArray = [12, 12, 12, 12, 3, 13, 13, 2, 2]
  

Мне нужен массив completeHbiScores = [12, 12, 12, 12, 13, 13]

Комментарии:

1. Рассмотрите возможность использования одного массива пользовательской структуры для модели, а не нескольких массивов.

2. ООО, это звучит супер круто, как я могу это сделать?

3. Руководство по языку Swift: классы и структуры

Ответ №1:

Если вам нужно использовать два массива, вы можете решить это с zip помощью , filter и map вот так:

 let hbiCompleteArray = [true, true, true, true, false, true, true, false, false]
let hbiScoreArray = [12, 12, 12, 12, 3, 13, 13, 2, 2]

let result = zip(hbiCompleteArray, hbiScoreArray).filter { $0.0 }.map { $1 }
print(result)
  

Дает:

[12, 12, 12, 12, 13, 13]

Объяснение: zip чередует два массива (создает массив (Bool, Int) кортежей), затем filter { $0.0 } сохраняет только true логические значения, затем map сохраняет только значения Int.

Комментарии:

1. Это хороший и довольно простой способ сделать это, если массивы не слишком большие. Единственная проблема заключается в том, что каждая из этих операций проходит через массивы, и поэтому она может застрять на больших массивах. Достаточно ли этого для оптимизации? Это вам решать, конечно, не выбирайте более сложное решение, если тестирование не показывает, что это слишком медленно для ваших нужд.

2. @ColGraff Это правда, что он повторяется несколько раз, но я думаю, что здесь это не проблема, даже не проблема. Мы можем предположить, что у OP не будет миллиардов баллов в их массиве — кроме того, природа Zip2Sequence (не фактический массив) и тот факт, что filter / map оптимизированы (буферы, копирование при записи, оптимизация компилятора и т. Д.), Заставляет меня сказать, что здесь действительно все в порядке.:)

3. Я согласен, что это, вероятно, не вызывает беспокойства, и переход на другое решение без тестирования узких мест определенно является преждевременной оптимизацией. Используйте это решение для его простоты и ясности, если тестирование показывает, что это узкое место, тогда оптимизируйте его. Это должно быть руководством для большинства разработчиков программного обеспечения!

4. Обратите внимание, что вы также можете использовать flatMap вместо filter и map , например let result = zip(hbiCompleteArray, hbiScoreArray).flatMap {$0 ? $1 : nil} 🙂

5. Работает хорошо и действительно элегантно. Попытка использовать тот же метод для возврата массива дат сейчас. Спасибо

Ответ №2:

Комментарий от vadian здесь очень важен. Таким образом, у вас не должно быть нескольких массивов. Создайте структуру, содержащую данные:

 struct Score {
    let isComplete: Bool
    let finalScore: Int
}
  

Затем вы можете добавить дату или любые другие поля, для которых у вас в настоящее время есть параллельные массивы. Тогда ваши данные выглядят так:

 let scores = [
    Score(isComplete: true, finalScore: 12),
    Score(isComplete: true, finalScore: 12),
    Score(isComplete: true, finalScore: 12),
    Score(isComplete: true, finalScore: 12),
    Score(isComplete: false, finalScore: 3),
    Score(isComplete: true, finalScore: 13),
    Score(isComplete: true, finalScore: 13),
    Score(isComplete: false, finalScore: 2),
    Score(isComplete: false, finalScore: 2),
]
  

И получить полные массивы просто путем фильтрации

 let completeScores = scores.filter { $0.isComplete }
  

Конечно, если вам нужны только окончательные результаты в виде массива, вы можете сопоставить их с этим:

 let finalCompleteScores = completeScores.map { $0.finalScore }
  

Вот как вы должны думать о своих данных, а не как о куче массивов, которые вы должны синхронизировать.

Комментарии:

1. Это, безусловно, оптимальное решение, если вы можете поместить свои данные в единую структуру, обычно это приводит к более чистому и простому в обслуживании коду. Если вам нужно иметь два массива по другим причинам проектирования, тогда другие решения могут работать лучше.

2. Мне нравится это решение, поскольку я также могу добавить дату оценки. Затем я могу использовать дату / оценку для графика. Я попробую это позже.

Ответ №3:

Согласитесь, что подход с параллельными массивами — не лучшая структура для вашего кода, но альтернативой фильтрам и сопоставлениям, используемым Eric, является reduce:

 let completeHbiScores = zip(hbiCompleteArray, hbiScoreArray).reduce([Int]()){
    (newArray,zippedArray) in
    if zippedArray.0 {
        return newArray   [zippedArray.1]
    }
    else {
        return newArray
    }}
  

Ответ №4:

Еще один довольно простой способ сделать это, который повторяется только один раз через ваши массивы::

 let hbiCompleteArray = [true, true, true, true, false, true, true, false, false]
let hbiScoreArray = [12, 12, 12, 12, 3, 13, 13, 2, 2]
var completeHbiScores = [Int]()

for score in hbiScoreArray.enumerated() {
  // break out of the loop if we're at the end of hbiCompleteArray
  // assuming that no value means default to false
  guard score.offset < hbiCompleteArray.count else { break }

  // go to the next score if the current element is false
  guard hbiCompleteArray[score.offset] else { continue }

  completeHbiScores.append(score.element)
}