Saltar al contenido

Cómo agrupar por los elementos de un array en Swift

Haz todo lo posible por entender el código correctamente antes de utilizarlo a tu trabajo y si tquieres aportar algo puedes compartirlo con nosotros.

Solución:

Rápido 4:

Desde Swift 4, esta funcionalidad se ha agregado a la biblioteca estándar. Puedes usarlo así:

Dictionary(grouping: statEvents, by:  $0.name )
[
  "dinner": [
    StatEvents(name: "dinner", date: "01-01-2015", hours: 1),
    StatEvents(name: "dinner", date: "01-01-2015", hours: 1),
    StatEvents(name: "dinner", date: "01-01-2015", hours: 1)
  ],
  "lunch": [
    StatEvents(name: "lunch", date: "01-01-2015", hours: 1),
    StatEvents(name: "lunch", date: "01-01-2015", hours: 1)
]

Rápido 3:

public extension Sequence 
    func group(by key: (Iterator.Element) -> U) -> [U:[Iterator.Element]] 
        var categories: [U: [Iterator.Element]] = [:]
        for element in self 
            let key = key(element)
            if case nil = categories[key]?.append(element) 
                categories[key] = [element]
            
        
        return categories
    

Desafortunadamente, el append la función anterior copia el subyacente array, en lugar de mutarlo en su lugar, lo que sería preferible. Esto provoca una desaceleración bastante grande. Puede solucionar el problema utilizando un contenedor de tipo de referencia:

class Box 
  var value: A
  init(_ val: A) 
    self.value = val
  


public extension Sequence 
  func group(by key: (Iterator.Element) -> U) -> [U:[Iterator.Element]] 
    var categories: [U: Box<[Iterator.Element]>] = [:]
    for element in self 
      let key = key(element)
      if case nil = categories[key]?.value.append(element) 
        categories[key] = Box([element])
      
    
    var result: [U: [Iterator.Element]] = Dictionary(minimumCapacity: categories.count)
    for (key,val) in categories 
      result[key] = val.value
    
    return result
  

Aunque recorra el diccionario final dos veces, esta versión sigue siendo más rápida que la original en la mayoría de los casos.

Rápido 2:

public extension SequenceType 

  /// Categorises elements of self into a dictionary, with the keys given by keyFunc

  func categorise(@noescape keyFunc: Generator.Element -> U) -> [U:[Generator.Element]] 
    var dict: [U:[Generator.Element]] = [:]
    for el in self 
      let key = keyFunc(el)
      if case nil = dict[key]?.append(el)  dict[key] = [el] 
    
    return dict
  

En su caso, podría tener el “keys”devuelto por keyFunc sean los nombres:

currentStat.statEvents.categorise  $0.name 
[  
  dinner: [
    StatEvents(name: "dinner", date: "01-01-2015", hours: 1),
    StatEvents(name: "dinner", date: "01-01-2015", hours: 1),
    StatEvents(name: "dinner", date: "01-01-2015", hours: 1)
  ], lunch: [
    StatEvents(name: "lunch", date: "01-01-2015", hours: 1),
    StatEvents(name: "lunch", date: "01-01-2015", hours: 1)
  ]
]

Entonces obtendrá un diccionario, donde cada key es un nombre, y cada valor es un array de StatEvents con ese nombre.

Rápido 1

func categorise(seq: S, @noescape keyFunc: S.Generator.Element -> U) -> [U:[S.Generator.Element]] 
  var dict: [U:[S.Generator.Element]] = [:]
  for el in seq 
    let key = keyFunc(el)
    dict[key] = (dict[key] ?? []) + [el]
  
  return dict


categorise(currentStat.statEvents)  $0.name 

Lo que da la salida:

extension StatEvents : Printable 
  var description: String 
    return "(self.name): (self.date)"
  

print(categorise(currentStat.statEvents)  $0.name )
[
  dinner: [
    dinner: 01-01-2015,
    dinner: 01-01-2015,
    dinner: 01-01-2015
  ], lunch: [
    lunch: 01-01-2015,
    lunch: 01-01-2015
  ]
]

(El Swiftstub está aquí)

Con Swift 5, Dictionary tiene un método de inicialización llamado init(grouping:by:). init(grouping:by:) tiene la siguiente declaración:

init(grouping values: S, by keyForValue: (S.Element) throws -> Key) rethrows where Value == [S.Element], S : Sequence

Crea un nuevo diccionario donde el keys son las agrupaciones devueltas por el cierre dado y los valores son matrices de los elementos que devolvieron cada específico key.


El siguiente código de Playground muestra cómo usar init(grouping:by:) para solucionar su problema:

struct StatEvents: CustomStringConvertible 
    
    let name: String
    let date: String
    let hours: Int
    
    var description: String 
        return "Event: (name) - (date) - (hours)"
    
    


let statEvents = [
    StatEvents(name: "lunch", date: "01-01-2015", hours: 1),
    StatEvents(name: "dinner", date: "01-01-2015", hours: 1),
    StatEvents(name: "lunch", date: "01-01-2015", hours: 1),
    StatEvents(name: "dinner", date: "01-01-2015", hours: 1)
]

let dictionary = Dictionary(grouping: statEvents, by:  (element: StatEvents) in
    return element.name
)
//let dictionary = Dictionary(grouping: statEvents)  $0.name  // also works  
//let dictionary = Dictionary(grouping: statEvents, by: .name) // also works

print(dictionary)
/*
prints:
[
    "dinner": [Event: dinner - 01-01-2015 - 1, Event: dinner - 01-01-2015 - 1],
    "lunch": [Event: lunch - 01-01-2015 - 1, Event: lunch - 01-01-2015 - 1]
]
*/

Swift 4: puedes usar init (agrupación: por 🙂 desde el sitio de desarrolladores de Apple

Ejemplo:

let students = ["Kofi", "Abena", "Efua", "Kweku", "Akosua"]
let studentsByLetter = Dictionary(grouping: students, by:  $0.first! )
// ["E": ["Efua"], "K": ["Kofi", "Kweku"], "A": ["Abena", "Akosua"]]

Entonces en tu caso

   let dictionary = Dictionary(grouping: currentStat.statEvents, by:   $0.name! )

Sección de Reseñas y Valoraciones

Si haces scroll puedes encontrar las ilustraciones de otros desarrolladores, tú además tienes la opción de insertar el tuyo si lo crees conveniente.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Respuestas a preguntas comunes sobre programacion y tecnología