Свести массив массивов в Swift

144

Есть ли в Swift аналог в flattenScala, Xtend, Groovy, Ruby и со?

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
}
Кристиан Дитрих
источник
разве это не похоже на использование объекта add массива?
Фам Хоан
Я еще не изучал сам Swift, но в Haskell и F # это `concat '- так может выглядеть что-то вроде этого? - Я довольно уверен, что это где-то там (большинство языков FP. Знают о монадах, и это связка Листа)
Карстен
да в haskell это на самом деле называется concat.
Кристиан Дитрих
Вы должны принять ответ andreschneider .
Роб

Ответы:

436

Swift> = 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())

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

andreschneider
источник
3
Просто, чтобы заявить это более широко, flatMapдоступно с Swift 1.2.
Мик МакКаллум
3
в чем разница joined(формально известная как flatten) с flatMap? Является ли это, что, хотя flatMapприсоединяется, он также может отображать / преобразовывать вещи. но здесь в примере мы действительно не нуждаемся, т.е. мы возвращаемся$0
Мед
6
@Dschee flatMapбудет либо сглаживается 2D - массив в массиве 1D или удалить nilзначения, а не оба. Он определяет, что делать, основываясь на том, Elementявляется ли массив 1-го уровня массивом или необязательным, поэтому, если вы передадите ему 2D-массив опций (например [[Int?]]), он выберет его выравнивание до 1D (например [Int?]) . Чтобы сгладить 1-D и удалить нули 2-го уровня, вам придется это сделать array.flatMap { $0 }.flatMap { $0 }. Другими словами, выравнивание размеров эквивалентно Array(array.joined())и «выравнивание» удаления нулей эквивалентно array.filter{ $0 != nil }.map{ $0! }.
Слипп Д. Томпсон
1
@Warpling flatMapпо-прежнему подходит для использования, описанного в вопросе (выравнивание двумерного массива до 1D). compactMapявно для удаления nilэлементов из последовательности, как вариант flatMapкогда-то сделал.
Джим
1
@mohamadrezakoohkan это правильно. Поскольку ваш массив имеет тип [[Any]], a 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
@Crisco большое спасибо за ваши предложения, но я запускаю отрывки, прежде чем публиковать их где-либо. И мой ответ правильный, поскольку он возвращает те же результаты, что и запрошенный OP, и использует для этого меньше кода. Я признаю, что мой первоначальный ответ возвращал ленивую коллекцию вместо массива, хотя в этом вопросе не было никаких ограничений. Я до сих пор не думаю, что удаление правильного ответа каким-либо образом улучшает качество вопроса.
Макс Десятов
Это была моя точка - что при тестировании / печать на выходе вы получаете массив массивов: FlattenBidirectionalCollection<Array<Array<Int>>>(_base: [[1, 2, 3], [4], [5, 6, 7, 8, 9]])). Ваша точка зрения верна, хотя вы можете обращаться к ней как к плоскому массиву, поэтому может показаться, что CustomStringConvertableреализация вводит в заблуждение. Ваш фрагмент кода был и до сих пор не прошел тест.
Крис Коновер
1
По состоянию на swift 3.0 flatten()был переименован вjoined()
Mr. Xcoder
16

Swift 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

Swift 4.2

Я написал простое расширение массива ниже. Вы можете использовать, чтобы сгладить массив, который содержит другой массив или элемент. в отличие от метода join ().

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()
RahmiBozdag
источник
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. Я не нашел способа поместить compactMap внутрь для nil и опций, потому что внутри метода 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

Swift 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 вы можете использовать this = Array (self.matrix! .Joined ())

dgalluccio
источник
-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)
}

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

Раджеш Шарма
источник