2016-08-12 2 views
1

У меня есть массив Тэй содержит данные, как это: [значение] # [режим] (режим не является обязательным)Split массив на массивы по значению Swift

Я хочу разделить этот массив массивов [значение] на [Режим ], но сохраняя ту же позицию для данных, так что может быть много массива для такого же режима:

например:

let array = ["23.88", "24", "30", "24.16#C", "25#C", "12#C", "24.44#O", "50#O" , "31", "40" , "44#C", "55#C"] 

// Result 

No mode ---> [23.88,24,30] 
mode = C ---> [24.16,25,12] 
mode = O ---> [24.44,50] 
No mode ---> [31,40] 
mode = C ---> [44,55] 

Я попробовал это расширение, но это не то, что я хочу

extension SequenceType { 
    func groupBy<U : Hashable>(@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 
    } 
} 

это дает мне результат:

No mode ---> [23.88,24,30,31,40] 
mode = C ---> [24.16,25,12,44,55] 
mode = O ---> [24.44,50] 
+0

Я попытался расширение GroupBy но не то, что я хочу. Я обновил свой пост – OuSS

+0

Swift словари требуют, чтобы ключи были уникальными. Если вы хотите сгенерировать свои результаты с помощью словаря так, как вы их разбили, вам нужно будет иметь карту ключей для массива массивов. –

+0

Помогите мне – OuSS

ответ

1

Вам нужно изменить groupBy расширение для группы в массив 2-кортежей, а не словарь, в котором первый элемент кортежа соответствует неспецифическому «ключу», а второй элемент кортежа представляет собой массив последующих элементов массива self, который может быть отнесен к данному ключу.

Модифицированный SequenceType расширение

extension SequenceType { 
    func groupBy<U : Comparable>(@noescape keyFunc: Generator.Element -> U) -> [(U,[Generator.Element])] { 
     var tupArr: [(U,[Generator.Element])] = [] 
     for el in self { 
      let key = keyFunc(el) 
      if tupArr.last?.0 == key { 
       tupArr[tupArr.endIndex-1].1.append(el) 
      } 
      else { 
       tupArr.append((key,[el])) 
      } 
     } 
     return tupArr 
    } 
} 

Отметим также, что в настоящее время достаточно, чтобы в расширении родовое U соответствует Comparable, так как мы используем только U элементы, как «поддельные» ключей в кортеже ,

призыв к расширению

С этой модификации, мы можем вызвать метод groupBy(..), как

let array = ["23.88", "24", "30", "24.16#C", "25#C", "12", "24.44", "50" , "31#O", "40#O" , "44#C", "55#C"] 

/* assuming we know the last character always describe the mode, 
    given one is included (#) */ 
let groupedArray: [(String,[String])] = array.groupBy { 
    guard $0.characters.contains("#") else { return "No mode" } 
    return "mode = " + String($0.characters.last!) 
} 

print(groupedArray) 
/* [("No mode", ["23.88", "24", "30"]), 
    ("mode = C", ["24.16#C", "25#C"]), 
    ("No mode", ["12", "24.44", "50"]), 
    ("mode = O", ["31#O", "40#O"]), 
    ("mode = C", ["44#C", "55#C"])] */ 

Удаление оригинальных режима маркировки (#X) из сгруппированных массива

Если вы как удалить исходные метки режима (#X) в результирующем массиве, вы можете применить дополнительную операцию map после вызова groupBy.

Удаление маркировки с результирующих значений, как String:

let groupedArrayClean = groupedArray.map { ($0.0, $0.1.map { 
    String($0.characters.prefixUpTo($0.characters.indexOf("#") ?? $0.characters.endIndex)) 
    }) 
} 

print(groupedArrayClean) 
/* [("No mode", ["23.88", "24", "30"]), 
    ("mode = C", ["24.16", "25"]), 
    ("No mode", ["12", "24.44", "50"]), 
    ("mode = O", ["31", "40"]), 
    ("mode = C", ["44", "55"])] */ 

Или, с результирующих значений, как Double:

let groupedArrayClean = groupedArray.map { ($0.0, $0.1.flatMap { 
    Double(
     String(($0.characters.prefixUpTo($0.characters.indexOf("#") ?? $0.characters.endIndex)))) 
    }) 
} 

print(groupedArrayClean) 
/* [("No mode", [23.879999999999999, 24.0, 30.0]), 
    ("mode = C", [24.16, 25.0]), 
    ("No mode", [12.0, 24.440000000000001, 50.0]), 
    ("mode = O", [31.0, 40.0]), 
    ("mode = C", [44.0, 55.0])] */ 

В качестве альтернативы: группы и очистки режима маркировки в одном прикованного вызова

Или, b groupBy др следует map сразу, без промежуточного задания:

let groupedArrayClean: [(String,[String])] = array.groupBy { 
    guard $0.characters.contains("#") else { return "No mode" } 
    return "mode = " + String($0.characters.last!) 
    } 
    .map { ($0.0, $0.1.map { 
     String($0.characters 
      .prefixUpTo($0.characters.indexOf("#") ?? $0.characters.endIndex)) 
     }) 
} 

(аналогично для полученного значения Double случая.)

+1

Пока это лучший ответ IMO. Добавляется к текущей функции OP без тонны кода и делает именно то, что он должен делать. –

+0

@ Хайден Холлиган Я должен согласиться. Подумал, когда я начал писать свой ответ, никакой функции, предоставляемой OP, не было. –

+0

@ShadowOf справедливая точка. Твоя делает то же самое и работает, но есть определенно больше кода, чем нужно, функция или нет. –

1

Посмотрите на этот код:

func newArray(incomeArray:[String], index: Int) -> [String: [String]] { 
    let modeArray = incomeArray[index].componentsSeparatedByString("#") 
    let currentMode = modeArray.count > 1 ? modeArray.last : "No mode" 
    var arr: [String] = [] 
    for i in index...incomeArray.count-1 { 
     let modeArray = incomeArray[i].componentsSeparatedByString("#") 
     let mode = modeArray.count > 1 ? modeArray.last : "No mode" 
     if mode == currentMode || (mode == "" && currentMode == "") { 
      arr.append(modeArray.first!) 
     } else { 
      break 
     } 
    } 
    return ["Mode = " + currentMode!: arr]; 
} 


let array: [String] = ["23.88", "24", "30", "24.16#C", "25#C", "12", "24.44", "50" , "31#O", "40#O" , "44#C", "55#C"] 

var index = 0 
while index < array.count { 
    let dict = newArray(array, index: index) 
    print(dict) 
    index += dict[Array(dict.keys).first!]!.count 
} 

результат:

["Mode = No mode": ["23.88", "24", "30"]] 
["Mode = C": ["24.16", "25"]] 
["Mode = No mode": ["12", "24.44", "50"]] 
["Mode = O": ["31", "40"]] 
["Mode = C": ["44", "55"]] 
+0

Это даст мне массив со всем #C-режимом, но я хочу сохранить тот же порядок значений. если #C разделены, тогда мне нужно 2 массива #C – OuSS

+0

Итак, вам нужен новый массив каждый раз при изменении #mode? – Igor

+0

Да, что я хочу – OuSS

2

Я предлагаю вам использовать массив с кортежами (для хранения ключей) , Составьте этот массив, а затем вы можете легко переназначить его для форматирования вам необходимо:

let array = ["23.88", "24", "30", "24.16#C", "25#C", "12#C", "24.44#O", "50#O" , "31", "40" , "44#C", "55#C"] 

let noModeTag = "#NoMode" 

func group(array: [String]) -> [(String, [Double])] 
{ 
    var result = [(String, [Double])]() 

    func addNextElement(number: Double?, _ mode: String?) { 
     guard let number = number, mode = mode else {fatalError("input format error")} 
     if result.last?.0 == mode { 
      var array = result.last!.1 
      array.append(number) 
      result[result.count - 1] = (mode, array) 
     } else { 
      result.append((mode, [number])) 
     } 
    } 

    for element in array { 
     if element.containsString("#") { 
      let separated = element.componentsSeparatedByString("#") 
      addNextElement(Double(separated.first ?? "_"), separated.last) 
     } else { 
      addNextElement(Double(element), noModeTag) 
     } 
    } 
    return result 
} 

print(group(array)) 
//[("#NoMode", [23.88, 24.0, 30.0]), ("C", [24.16, 25.0, 12.0]), ("O", [24.44, 50.0]), ("#NoMode", [31.0, 40.0]), ("C", [44.0, 55.0])] 
+1

Благодарим за помощь – OuSS

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