2016-02-18 14 views
3

У меня есть 2 вида struct, Header и Session, оба соответствуют протоколу TimelineItem.
У меня есть Array состоит из TimelineItem так:
[Header1, SessionA, SessionB, Header2, SessionC, SessionD]Swift: преобразование массива [A, B, B, B, A, B, B, B] в массив хэш [[A: [B, B, B], [A: [B, B, B ]]]

Моей потребности в группу Session под соответствующей Header, как это:
[ [Header1: [SessionA, SessionB], [Header2: [SessionC, SessionD] ]

Я попытался с помощью filter метода для получения только Header-структуры, и split метод для извлечения массива Session Массив. Они работают нормально, но я не могу справиться с тем, как согласовать оба, чтобы построить мой последний объект [[Header: [Session]]].

Вот мой пример кода:

enum TimelineItemType: String { 
    case Header = "header" 
    case Session = "session" 
} 

protocol TimelineItem { 
    var id: Int { get } 
    var type: TimelineItemType { get } 
    var startDate: NSDate { get } 
} 

Header структура

struct Header: TimelineItem, Decodable, Hashable, Equatable { 
    let id: Int 
    let type: TimelineItemType = .Header 
    let startDate: NSDate 
    let text: String 

    init?(json: JSON) { 
    guard let id: Int = "id" <~~ json, 
     let type: TimelineItemType = "type" <~~ json, 
     let startDate: NSDate = "startDate" <~~ json, 
     let text: String = "text" <~~ json where type == .Header else { 
     return nil 
    } 
    self.id = id 
    self.startDate = startDate 
    self.text = text 
    } 

    var hashValue: Int { 
    // As id is unique, we can use it for hash purpose 
    return id 
    } 
} 

Session структура

struct Session: TimelineItem, Decodable, Equatable { 
    let id: Int 
    let type: TimelineItemType = .Session 
    let startDate: NSDate 
    let name: String 
    let syllabus: String 
    let speaker: Speaker 
    let language: String 
    let room: String 
    let duration: Int 

    init?(json: JSON) { 
    guard let id: Int = "id" <~~ json, 
     let type: TimelineItemType = "type" <~~ json, 
     let startDate: NSDate = Decoder.decodeDateISO8601("startDate")(json), 
     let name: String = "name" <~~ json, 
     let speaker: Speaker = "speaker" <~~ json, 
     let syllabus: String = "syllabus" <~~ json, 
     let language: String = "language" <~~ json, 
     let room: String = "room" <~~ json, 
     let duration: Int = "duration" <~~ json where type == .Session else { 
     return nil 
    } 
    self.id = id 
    self.startDate = startDate 
    self.name = name 
    self.speaker = speaker 
    self.syllabus = syllabus 
    self.language = language 
    self.room = room 
    self.duration = duration 
    } 
} 

И, наконец, код, который я пытался разделить мой массив:

func timelineFromItems(timelineItems: [TimelineItem]) -> [[Header: [Session]]]? { 

    let slicedSessions = timelineItems.split { $0 is Header } 
    let sessions = Array(slicedSessions) 
    let headers = timelineItems.filter { $0.type == .Header } 
    var timeline = [[Header: [Session]]]() 
    // HOW TO FILL THE TIMELINE ?? 
} 

КАК ЗАПОЛНИТЬ ВРЕМЯ?

ответ

1

уменьшает этот пример, удалив все не необходимую информацию

protocol P {} 
struct A: P, Hashable { 
    var i:Int 
    var hashValue: Int { return i } 
} 
func ==(lhs: A, rhs: A)->Bool { 
    return lhs.i == rhs.i 
} 
struct B: P { 
    var i:Int 
} 

// your current data 
let arr:[P] = [A(i: 1),B(i: 1),B(i: 2), A(i: 2), B(i: 3), B(i: 4), B(i: 5)] 

функция, которая преобразует данные в требуемый формат

func foo(arr: [P])->[[A:[B]]]? { 

    var dict:[A:[B]] = [:] 
    var arrb:[B] = [] 
    let arrk:[A] = arr.filter { $0 is A }.map { $0 as! A } 
    guard var key = arr[0] as? A else { return nil } 

    arr.forEach { (p) in 

     if let a = p as? A { 
      dict[key] = arrb 
      arrb = [] 
      key = a 
     } 
     if let b = p as? B { 
      arrb.append(b) 
     } 
    } 
    dict[key] = arrb 
    var arrr:[[A:[B]]] = [] 
    arrk.forEach { (a) in 
     if let arrb = dict[a] { 
      arrr.append([a:arrb]) 
     } 
    } 
    return arrr 
} 

теперь результирующий массив соответствует вашим требованиям (я надеюсь: -))

if let result = foo(arr) { 
    print(result) // [[A(i: 1): [B(i: 1), B(i: 2)]], [A(i: 2): [B(i: 3), B(i: 4), B(i: 5)]]] 
} 

другие данные испытаний

let arr:[P] = [A(i: 1),B(i: 1),B(i: 2), A(i: 2), A(i: 3), B(i: 3)] 

дает

[[A(i: 1): [B(i: 1), B(i: 2)]], [A(i: 2): []], [A(i: 3): [B(i: 3)]]] 

так, он работает, даже если нет B не следует

1

Я не вижу никаких препятствий при заполнении шкалы, вот как я бы это сделать:

func timelineFromItems(timelineItems: [TimelineItem]) -> [[Header: [Session]]]? { 

    let slicedSessions = timelineItems.split { $0 is Header } 
    let sessions = Array(slicedSessions) 
    let headers = timelineItems.filter { $0.type == .Header } 
    var timeline = [[Header: [Session]]]() 

    if sessions.count == headers.count { // Check to be sure you have as much header as sessions 
     for (index, value) in headers.enumerate() { 
      let dictionary = [value: sessions.getElement(index)] 
      timeline.append(dictionary) 
     } 
    } 

    return timeline 
} 
+0

Существует недостающее открытие' { 'в' for' заявлении. – Gl0ub1l

+1

Спасибо за исправление, я отредактировал ответ –

+0

Juste попробовал ваше решение, которое кажется действительно закрытым для того, что я хотел бы иметь. Но у меня есть ошибка компиляции: «Неоднозначная ссылка на индекс» в 'session [index]'. – Gl0ub1l

1

Вы можете на самом деле пойти с чем-то же просто, как:

func deserialize(input: [TimelineItem]) -> [Header: [Session]] { 
    var result = [Header: [Session]]() 

    var latestHeader: Header! = nil 

    input.forEach() { 
     if let header = $0 as? Header { 
      latestHeader = header 
      result[header] = [] 
     } else if let session = $0 as? Session { 
      result[latestHeader]!.append(session) 
     } 
    } 

    return result 
} 

UPD

Не так просто, но все же кратким (выше производит словарь с [Session] в качестве элементов, в то время, как @ user3441734 указал вам нужен массив одноэлементных словарей):

func deserialize(input: [TimelineItem]) -> [[Header: [Session]]] { 
    var result = [[Header: [Session]]]() 

    var latestHeader: Header? = nil 

    input.forEach() { 
     if let header = $0 as? Header { 
      latestHeader = header 
      result.append([header: []]) 
     } else if let session = $0 as? Session, let header = latestHeader { 
      var headerDict = result.popLast()! 
      headerDict[header]!.append(session) 
      result.append(headerDict) 
     } 
    } 

    return result 
} 
+0

ваш результат - словарь, поэтому неупорядоченная коллекция. моя первая попытка была такой же, до того, как я понял, что ему нужна упорядоченная коллекция/массив словарей, где словарь содержит только один ключ и одно связанное значение (массив)/... Array <[Header: [Session]]> – user3441734

+0

О да. Виноват. Поэтому ему нужен массив одноэлементных словарей. Позвольте мне настроить код тогда. – courteouselk

+0

Я сделал это, см. Мой ответ. Тем не менее, код «не самый лучший», как вы можете видеть, он работает так, как ожидалось :-) – user3441734

-2

Благодаря всем вам, мне удалось соответствовать моей потребности с кодом ниже:

`` `

typealias Section = [header: Header, sessions: [TimelineItem]] 

private static func groupBySection(timelineItems: [TimelineItem]) -> [Section]? { 
    let slicedSessions = timelineItems.split { $0.type == .Header } 
    let headers = timelineItems.filter { $0.type == .Header } 
    var timeline = [Section]() 

    for (index, value) in headers.enumerate() { 
     guard index < slicedSessions.count else { 
     break 
     } 

     let sessionIndex = slicedSessions.startIndex.advancedBy(index) 
     let arraySessions = Array(slicedSessions[sessionIndex]) 
     let header = value as! Header 
     let section = (header: header, sessions: arraySessions) 
     timeline.append(section) 
    } 
    return timeline.count > 0 ? timeline : nil 
    } 

`` `

Смежные вопросы