Swift: Funções de Ordem Superior do Swift

Funções de ordem superior são funções que aceitam outras funções como argumentos ou retornam funções. A biblioteca padrão do Swift fornece funções de ordem superior poderosas como map, filter e reduce, permitindo processar coleções em um estilo declarativo.

1. O Que Você Vai Aprender


2. Uma História Real de um Analista de Dados

(1) Problema: Código de Laço for Inchado para Processamento de Array

Bob precisa extrair usuários ativos de um array, ordená-los por idade e formatar as strings de saída:

SWIFT
var activeUsers: [String] = []
for user in users {
    if user.isActive {
        activeUsers.append(user.name)
    }
}
activeUsers.sort()
var result: [String] = []
for name in activeUsers {
    result.append("User: \(name)")
}

Dois laços for + um array temporário + uma ordenação. A lógica é simples, mas o código é longo e cheio de detalhes de "Como" em vez de expressar "O quê".

(2) A Solução com Funções de Ordem Superior

Use encadeamento funcional para realizar tudo em uma linha:

SWIFT
let result = users
    .filter { $0.isActive }
    .map { $0.name }
    .sorted()
    .map { "User: \($0)" }

Código vai de 10 linhas para 4, com melhor legibilidade.

(3) Benefício: As Vantagens da Programação Declarativa

Dimensão Laço for Cadeia de Ordem Superior
Tamanho do Código 10-15 linhas 3-5 linhas
Legibilidade Ler o corpo do laço para entender a intenção Os nomes das funções expressam diretamente a intenção
Fluxo de Dados Rastrear variáveis intermediárias A cadeia mostra claramente o fluxo de dados
Impacto de Mudanças Reescrever o laço para mudanças de lógica Adicionar/remover etapas sem afetar outras
Concorrência Gerenciamento manual Inerentemente favorável a paralelismo (ex.: parallelMap)

3. map: Transformar Cada Elemento

map aplica uma transformação a cada elemento em um array e retorna um novo array do mesmo comprimento.

100%
graph LR
    A["[1, 2, 3, 4, 5]"] --> B["map { $0 * 2 }"]
    B --> C["[2, 4, 6, 8, 10]"]
Cenário Entrada Transformação Saída
Dobrar valores [1, 2, 3] { $0 * 2 } [2, 4, 6]
Extrair propriedade [Usuario, Usuario] { $0.nome } ["Alice", "Bob"]
Conversão de tipo ["1", "2"] { Int($0) } [1, 2] (tipo se torna [Int?])

▶ Exemplo: Uso Básico do map

SWIFT
// ============================================
// map: Apply transformation to each element
// ============================================
let numbers = [1, 2, 3, 4, 5]
// for-loop approach (old style)
var doubled: [Int] = []
for n in numbers { doubled.append(n * 2) }
// map approach (modern style)
let doubledMap = numbers.map { $0 * 2 }
print("doubledMap: \(doubledMap)")
// Extract string lengths
let words = ["Swift", "Python", "Go"]
let lengths = words.map { $0.count }
print("lengths: \(lengths)")

Saída:

TEXT 📖 Somente leitura
doubledMap: [2, 4, 6, 8, 10]
lengths: [5, 6, 2]

4. filter: Selecionar Elementos

filter mantém os elementos que satisfazem uma condição, retornando um novo array.

▶ Exemplo: Operações de Seleção com filter

SWIFT
// ============================================
// filter: Keep elements that satisfy a condition
// ============================================
let scores = [45, 82, 91, 63, 77, 55, 88]
// Filter passing scores (>= 60)
let passed = scores.filter { $0 >= 60 }
print("Passed: \(passed)")
// Filter excellent scores (>= 85)
let excellent = scores.filter { $0 >= 85 }
print("Excellent: \(excellent)")
// Chaining: filter failing scores then double them
let failedDoubled = scores
    .filter { $0 < 60 }
    .map { $0 * 2 }
print("Failed scores doubled: \(failedDoubled)")

Saída:

TEXT 📖 Somente leitura
Passed: [82, 91, 63, 77, 88]
Excellent: [91, 88]
Failed scores doubled: [90, 110]

Dica: filter retorna um novo array preservando a ordem original.


5. reduce e compactMap

(1) reduce: Combinar em um Único Valor

reduce combina todos os elementos de um array em um único valor.

Parâmetro Significado Exemplo
resultadoInicial Valor inicial 0 para soma, "" para concatenação
proximoResultadoParcial Closure acumuladora { $0 + $1 }
100%
graph LR
    A["[1, 2, 3, 4]"] --> B["reduce(0) { $0 + $1 }"]
    B --> C["0+1=1, 1+2=3, 3+3=6, 6+4=10"]
    C --> D["10"]

▶ Exemplo: Vários Casos de Uso do reduce

SWIFT
// ============================================
// reduce: Reduction operations
// ============================================
let numbers = [1, 2, 3, 4, 5]
// Sum
let sum = numbers.reduce(0) { $0 + $1 }
print("sum: \(sum)")
// Product
let product = numbers.reduce(1) { $0 * $1 }
print("product: \(product)")
// Concatenate strings
let words = ["Swift", "is", "awesome"]
let sentence = words.reduce("") { $0.isEmpty ? $1 : "\($0) \($1)" }
print("sentence: \(sentence)")
// Named parameters for clarity
let sumNamed = numbers.reduce(0) { total, next in total + next }
print("sumNamed: \(sumNamed)")

Saída:

TEXT 📖 Somente leitura
sum: 15
product: 120
sentence: Swift is awesome
sumNamed: 15

(2) compactMap: Filtrar Valores nil

compactMap funciona como map, mas filtra automaticamente os valores nil.

SWIFT
// ============================================
// compactMap and flatMap
// ============================================
// compactMap: Filter nil
let strings = ["1", "two", "3", "four", "5"]
let numbers2 = strings.compactMap { Int($0) }
print("numbers: \(numbers2)")
// flatMap: Flatten nested arrays
let nested = [[1, 2], [3, 4, 5], [6]]
let flattened = nested.flatMap { $0 }
print("flattened: \(flattened)")

Saída:

TEXT 📖 Somente leitura
numbers: [1, 3, 5]
flattened: [1, 2, 3, 4, 5, 6]

6. Encadeamento na Prática

O uso mais poderoso das funções de ordem superior é o encadeamento — conectar múltiplas operações em um pipeline.

▶ Exemplo: Cadeia de Transformação de Dados Complexa

SWIFT
// ============================================
// Chaining: User data analysis pipeline
// ============================================
struct User {
    let name: String
    let age: Int
    let isActive: Bool
    let scores: [Int]
}
let users = [
    User(name: "Alice", age: 28, isActive: true, scores: [85, 90, 78]),
    User(name: "Bob", age: 22, isActive: false, scores: [70, 75]),
    User(name: "Charlie", age: 35, isActive: true, scores: [95, 92, 98, 100]),
    User(name: "Diana", age: 17, isActive: true, scores: [60, 65]),
    User(name: "Eve", age: 30, isActive: false, scores: [88, 82])
]
// Chain pipeline: active users → adults → average score → sort by score → format
let report = users
    .filter { $0.isActive }
    .filter { $0.age >= 18 }
    .map { user -> (name: String, avgScore: Double) in
        let avg = Double(user.scores.reduce(0, +)) / Double(user.scores.count)
        return (user.name, avg)
    }
    .sorted { $0.avgScore > $1.avgScore }
    .map { "\($0.name): \(String(format: "%.1f", $0.avgScore))" }
print("=== Active Adult User Report ===")
report.forEach { print($0) }

Saída:

TEXT 📖 Somente leitura
=== Active Adult User Report ===
Charlie: 96.2
Alice: 84.3

7. Exemplo Completo: Calculadora de Preços do Carrinho de Compras

SWIFT
// ============================================
// Complete example: Shopping cart price calculator
// Features: map + filter + reduce + compactMap combined
// ============================================
import Foundation
struct CartItem {
    let name: String
    let price: Double
    let quantity: Int
    let isDiscounted: Bool
}
let cart = [
    CartItem(name: "Laptop", price: 1299, quantity: 1, isDiscounted: false),
    CartItem(name: "Mouse", price: 29.99, quantity: 2, isDiscounted: true),
    CartItem(name: "Keyboard", price: 99.99, quantity: 1, isDiscounted: true),
    CartItem(name: "Cable", price: 9.99, quantity: 3, isDiscounted: false),
    CartItem(name: "Monitor", price: 399, quantity: 0, isDiscounted: false)
]
// 1. Filter out zero-quantity items
let validItems = cart.filter { $0.quantity > 0 }
// 2. Calculate total per item (15% off for discounted items)
let itemTotals = validItems.map { item -> (String, Double) in
    let unitPrice = item.isDiscounted ? item.price * 0.85 : item.price
    let total = unitPrice * Double(item.quantity)
    return (item.name, total)
}
// 3. Calculate grand total
let grandTotal = itemTotals.reduce(0) { $0 + $1.1 }
// 4. Format output
print("=== Shopping Cart ===")
itemTotals.forEach { print("  \($0): $\(String(format: "%.2f", $1))") }
print("  ----------")
print("  Total: $\(String(format: "%.2f", grandTotal))")

Saída:

TEXT 📖 Somente leitura
=== Shopping Cart ===
 Laptop: $1299.00
 Mouse: $50.98
 Keyboard: $84.99
 Cable: $29.97
  ----------
  Total: $1464.94

❓ Perguntas Frequentes

P: Qual é a diferença entre map e forEach? R: map retorna um novo array; forEach apenas itera sem retornar. Use map quando quiser transformar dados em um novo array. Use forEach apenas para efeitos colaterais (como imprimir). P: Qual é a diferença entre flatMap e compactMap? R: No Swift 4.1+, flatMap achata coleções aninhadas (ex.: [[1,2], [3]][1,2,3]), enquanto compactMap filtra valores nil (ex.: [1, nil, 3][1, 3]). Cada um tem seu próprio papel. P: O encadeamento prejudica o desempenho? R: Cada chamada cria um novo array, então há custos de alocação intermediária. Para dados de pequeno a médio porte (alguns milhares de elementos ou menos), o impacto é insignificante. Para grandes conjuntos de dados, considere usar lazy para avaliação adiada: array.lazy.filter { ... }.map { ... }. P: O reduce pode lidar com arrays vazios? R: Sim. reduce simplesmente retorna o valor inicial. Por exemplo, [].reduce(0, +) retorna 0. No entanto, se o valor inicial não for apropriado para arrays vazios (ex.: encontrar um máximo), verifique isEmpty primeiro. P: As funções de ordem superior podem substituir todos os laços for? R: Na maioria das vezes, sim. Mas laços for são melhores para certos casos: quando você precisa de break no meio, precisa de índices para operações complexas ou precisa acessar elementos adjacentes. Use funções de ordem superior para: transformar, filtrar, reduzir, achatar.


📖 Resumo


📝 Exercícios

  1. Básico: Dado o array de inteiros [10, 20, 30, 40, 50], primeiro use map para dividir cada elemento por 2, depois use filter para manter elementos maiores que 15.
  2. Intermediário: Dado o array de strings ["abacaxi", "banana", "kiwi", "morango", "figo"], use encadeamento para filtrar strings com comprimento >= 5, converter para maiúsculas e ordenar alfabeticamente.
  3. Desafio: Dado um array de structs Transacao (propriedades: valor: Double, categoria: String, data: Date), use reduce(into:) para agrupar e somar valores por categoria. Em seguida, exiba o total de cada categoria.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%