Зрівняти масив масивів у Swift


144

Чи є аналог у Swift to flattenу Scala, Xtend, Groovy, Ruby та co?

var aofa = [[1,2,3],[4],[5,6,7,8,9]]
aofa.flatten() // shall deliver [1,2,3,4,5,6,7,8,9] 

Звичайно, я міг би використати зменшення для цього, але це якось смокче

var flattened = aofa.reduce(Int[]()){
    a,i in var b : Int[] = a
    b.extend(i)
    return b
}

Хіба це не так, як використовувати об’єкт Додати масив?
Фам Хоан

Я ще не заглянув у Свіфт, але в Haskell і F # це `concat` - то, можливо, виглядаю щось на зразок цього? - Я досить впевнений, що це є десь (більшість FP мовчать. Знають про монадів, і це прив'язка Ліста)
Карстен

так, у haskell це насправді називається concat.
Крістіан Дітріх

Ви повинні прийняти відповідь andreschneider .
Роб

Відповіді:


436

Швидкий> = 3.0

reduce:

let numbers = [[1,2,3],[4],[5,6,7,8,9]]
let reduced = numbers.reduce([], +)

flatMap:

let numbers = [[1,2,3],[4],[5,6,7,8,9]]
let flattened = numbers.flatMap { $0 }

joined:

let numbers = [[1,2,3],[4],[5,6,7,8,9]]
let joined = Array(numbers.joined())

введіть тут опис зображення


3
Просто для констатації цього більш загальнодоступного, flatMapдоступного у Swift 1.2
Мік Маккаллум,

3
у чому різниця між joined(формально відомим як flatten) flatMap? Це те, що flatMapприєднуючись, він також може відображати / перетворювати речі. але ось у прикладі нам насправді не потрібно, тобто ми повертаємось$0
Мед

6
@Dschee flatMapбуде або згладжується 2D - масив в масиві 1D або видалити nilзначення, а з обидва. Він визначає, що робити на основі, якщо масив 1-го рівня Element- це масив чи необов'язковий, тому, якщо ви передасте йому 2D масив необов'язкових (наприклад [[Int?]]), він вирішить вирівняти його до 1D (наприклад [Int?]) . Для того, щоб вирівняти 1-D і видалити нулі 2-го рівня, вам доведеться це зробити array.flatMap { $0 }.flatMap { $0 }. Іншими словами, вирівнювання розмірів еквівалентно, Array(array.joined())а «сплющення» зняття нуля дорівнює array.filter{ $0 != nil }.map{ $0! }.
Сліпп Д. Томпсон

1
@ Warpling flatMapяк і раніше підходить для використання, описаного у запитанні (вирівнювання 2D масиву до 1D). compactMapє явним чином для видалення nilелементів із послідовності, як варіант flatMapодного разу.
Джим Дові

1
@mohamadrezakoohkan це правильно. Оскільки ваш масив має тип [[Any]], flatMapпросто перетворює його на тип [Any]([1, 2, 3, 4, [5, 6], 7, 8, 9]). І якщо ми flatMapзнову застосуємо , ми б діяли на "Будь-який? тип, де компілятор більше не знає, чи це просте значення або сам масив.
andreschneider

31

У стандартній бібліотеці Swift є joinedфункція, реалізована для всіх типів, що відповідають Sequenceпротоколу (або flattenввімкнено SequenceTypeдо Swift 3), що включає Array:

let numbers = [[1,2,3],[4],[5,6,7,8,9]]
let flattened = Array(numbers.joined())

У деяких випадках використання joined()може бути корисним, оскільки воно повертає ледачу колекцію замість нового масиву, але завжди може бути перетворене в масив при передачі на Array()ініціалізатор, як у прикладі вище.


@chrisco Ви можете, будь ласка, пояснити, як моя відповідь невірна та які критерії "найпростішої правильної відповіді"? Чи можете ви також сказати, як видалення відповіді може вплинути на питання будь-яким чином?
Макс Десятов

Спробуйте спершу запустити фрагмент - як ви думаєте, що це робить? Що це насправді робить? Яким було первісне питання? Правильна ваша відповідь? Якщо ні, то краще буде видалити його, щоб поліпшити чіткість цієї публікації. Я зробив те саме з неправильними власними відповідями.
Кріс Коновер

1
@chrisco дуже дякую за ваші пропозиції, але я запускаю фрагменти, перш ніж публікувати їх у будь-якому місці. І моя відповідь правильна, оскільки вона повертає абсолютно ті самі результати, що і запитувала ОП, і використовуючи для цього менше коду. Я визнаю, що моя первісна відповідь повертала ледачу колекцію замість масиву, хоча в питанні жодних обмежень щодо цього не було. Я все ще не думаю, що видалення правильної відповіді жодним чином покращує якість запитання.
Макс Десіатов

Це була моя точка - що при тестуванні / друк на виході ви отримуєте масив масивів: FlattenBidirectionalCollection<Array<Array<Int>>>(_base: [[1, 2, 3], [4], [5, 6, 7, 8, 9]])). Ваша точка дійсна, хоча ви можете отримати доступ до неї як плоский масив, тому здавалося б, що CustomStringConvertableреалізація вводить в оману. Ваш фрагмент коду був і все ще не вистачає тесту.
Кріс Коновер

1
Станом на швидкий 3.0, flatten()він був перейменований вjoined()
Містер Xcoder

16

Швидкий 4.x / 5.x

Просто для додання трохи більшої складності в масиві, якщо є масив, який містить масив масивів, то flatMapнасправді вийде з ладу.

Припустимо, масив є

var array:[Any] = [1,2,[[3,4],[5,6,[7]]],8]

Що flatMapабо compactMapповертає:

array.compactMap({$0})

//Output
[1, 2, [[3, 4], [5, 6, [7]]], 8]

Для вирішення цієї проблеми ми можемо використовувати нашу просту для логіки циклу + рекурсію

func flattenedArray(array:[Any]) -> [Int] {
    var myArray = [Int]()
    for element in array {
        if let element = element as? Int {
            myArray.append(element)
        }
        if let element = element as? [Any] {
            let result = flattenedArray(array: element)
            for i in result {
                myArray.append(i)
            }

        }
    }
    return myArray
}

Тому викликайте цю функцію за допомогою заданого масиву

flattenedArray(array: array)

Результат:

[1, 2, 3, 4, 5, 6, 7, 8]

Ця функція допоможе вирівняти масив будь-якого типу, враховуючи випадок Int тут

Вихід на майданчик: введіть тут опис зображення




2

Швидкий 4.2

Нижче я написав просте розширення масиву. Ви можете використовувати для вирівнювання масиву, який містить інший масив або елемент. на відміну від методу joined ()

public extension Array {
    public func flatten() -> [Element] {
        return Array.flatten(0, self)
    }

    public static func flatten<Element>(_ index: Int, _ toFlat: [Element]) -> [Element] {
        guard index < toFlat.count else { return [] }

        var flatten: [Element] = []

        if let itemArr = toFlat[index] as? [Element] {
            flatten = flatten + itemArr.flatten()
        } else {
            flatten.append(toFlat[index])
        }

        return flatten + Array.flatten(index + 1, toFlat)
    }
}

використання:

let numbers: [Any] = [1, [2, "3"], 4, ["5", 6, 7], "8", [9, 10]]

numbers.flatten()

1

Ще одна загальна реалізація reduce,

let numbers = [[1,2,3],[4],[5,6,7,8,9]]
let reduced = reduce(numbers,[],+)

Це робить те саме, але може дати більше розуміння того, що відбувається reduce.

З документів Apple,

func reduce<S : SequenceType, U>(sequence: S, initial: U, combine: (U, S.Generator.Element) -> U) -> U

Опис

По черзі повертайте результат багаторазового виклику комбінації з накопиченим значенням, ініціалізованим на початковий, і кожен елемент послідовності


З вашим кодом я отримую:Use of unresolved identifier 'reduce'
Джейсон Мур

1

Модифікована відповідь @ RahmiBozdag, 1. Методи у публічних розширеннях є загальнодоступними. 2. Видалено додатковий метод, оскільки індекс запуску завжди буде нульовим. 3. Я не знайшов способу помістити компакт-карту всередину для нуля та необов'язкових, тому що всередині методу T завжди [Any?], Будь-які пропозиції вітаються.

 let array = [[[1, 2, 3], 4], 5, [6, [9], 10], 11, nil] as [Any?]

 public extension Array {

 func flatten<T>(_ index: Int = 0) -> [T] {
        guard index < self.count else { 
            return [] 
        }

        var flatten: [T] = []

        if let itemArr = self[index] as? [T] {
            flatten += itemArr.flatten()
        } else if let element = self[index] as? T {
            flatten.append(element)
        }
        return flatten + self.flatten(index + 1)
   }

}

let result: [Any] = array.flatten().compactMap { $0 }
print(result)
//[1, 2, 3, 4, 5, 6, 9, 10, 11]

0

Ви можете вирівняти вкладений масив, використовуючи наступний метод:

var arrays = [1, 2, 3, 4, 5, [12, 22, 32], [[1, 2, 3], 1, 3, 4, [[[777, 888, 8999]]]]] as [Any]

func flatten(_ array: [Any]) -> [Any] {

    return array.reduce([Any]()) { result, current in
        switch current {
        case(let arrayOfAny as [Any]):
            return result + flatten(arrayOfAny)
        default:
            return result + [current]
        }
    }
}

let result = flatten(arrays)

print(result)

/// [1, 2, 3, 4, 5, 12, 22, 32, 1, 2, 3, 1, 3, 4, 777, 888, 8999]

0

Версія Apple Swift 5.1.2 (swiftlang-1100.0.278 clang-1100.0.33.9)
Мета: x86_64-apple-darwin19.2.0

Знімок екрана

let optionalNumbers = [[1, 2, 3, nil], nil, [4], [5, 6, 7, 8, 9]]
print(optionalNumbers.compactMap { $0 }) // [[Optional(1), Optional(2), Optional(3), nil], [Optional(4)], [Optional(5), Optional(6), Optional(7), Optional(8), Optional(9)]]
print(optionalNumbers.compactMap { $0 }.reduce([], +).map { $0 as? Int ?? nil }.compactMap{ $0 }) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(optionalNumbers.compactMap { $0 }.flatMap { $0 }.map { $0 as? Int ?? nil }.compactMap{ $0 }) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(Array(optionalNumbers.compactMap { $0 }.joined()).map { $0 as? Int ?? nil }.compactMap{ $0 }) // [1, 2, 3, 4, 5, 6, 7, 8, 9]

let nonOptionalNumbers = [[1, 2, 3], [4], [5, 6, 7, 8, 9]]
print(nonOptionalNumbers.compactMap { $0 }) // [[1, 2, 3], [4], [5, 6, 7, 8, 9]]
print(nonOptionalNumbers.reduce([], +)) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(nonOptionalNumbers.flatMap { $0 }) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(Array(nonOptionalNumbers.joined())) // [1, 2, 3, 4, 5, 6, 7, 8, 9]

0

Швидкий 5.1

public extension Array where Element: Collection {

    func flatten() -> [Element.Element] {
        return reduce([], +)
    }
}

Якщо ви також хочете його для значень словника:

public extension Dictionary.Values where Value : Collection {
    func flatten() -> [Value.Element]{
         return self.reduce([], +)
    }
}


-1

матриця є [[myDTO]]?

У swift 5 ви можете використовувати цей = масив (self.matrix! .Joined ())


-2
func convert(){
    let arr = [[1,2,3],[4],[5,6,7,8,9]]
    print("Old Arr = ",arr)
    var newArr = [Int]()
    for i in arr{
        for j in i{
            newArr.append(j)
        }
    }
    print("New Arr = ",newArr)
}

введіть тут опис зображення

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.