Swiftで配列の配列をフラット化する


142

Swift 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] 

もちろん、そのためにreduceを使用することもできますが、それはちょっと困ります

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

配列のオブジェクトの追加を使用するようなものではありませんか?
Pham Hoan 2014年

私はまだSwift自体を調べていませんが、HaskellとF#では `concat`なので、次のような名前になりますか?-これがどこかにあることはかなりポジティブです(ほとんどのFP言語。モナドについて知っており、これはリストのバインドです)
Carsten

はい、haskellでは実際にはconcatと呼ばれています。
クリスチャンディートリッヒ


回答:


435

スウィフト> = 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 以降で使用できます。
Mick MacCallum、2015

3
joined(正式にはとして知られているflatten)との違いは何flatMapですか?flatMap参加している間、それは物事をマップ/変換することもできますか?しかし、ここの例では本当に必要ありません。つまり、戻ります$0
Honey

6
@Dscheeは、flatMapあろういずれかの 1次元配列に2次元アレイを平坦化または削除nilの値、両方ではありません。これは、第1レベルの配列Elementが配列であるかオプションであるかに基づいてどちらを行うかを決定します。そのため、オプションの2D配列(例[[Int?]]を渡す場合は、1D (例[Int?]にフラット化することを選択します。1-Dに平坦化し、2番目のレベルのnilを削除するには、を実行する必要がありますarray.flatMap { $0 }.flatMap { $0 }。言い換えると、次元の平坦化はArray(array.joined())と等しく、除去なしの「平坦化」はと同等array.filter{ $0 != nil }.map{ $0! }です。
Slipp D. Thompson

1
@Warpling flatMapは、質問で説明されている使用法(2D配列を1Dにフラット化)にも適しています。かつてのバリエーションと同様に、シーケンスからアイテムをcompactMap明示的に削除するためのnilものflatMapです。
Jim Dovey

1
@mohamadrezakoohkan正解です。配列のタイプはであるため[[Any]]、はflatMap単純に[Any]([1、2、3、4、[5、6]、7、8、9])のタイプに変換します。また、flatMap再度申請する場合は、「Any?型。単純な値なのか、配列そのものなのか、コンパイラーは知りません。
andreschneider

31

Swift標準ライブラリにはjoinedSequenceプロトコルに準拠した(またはSwift 3 flattenよりSequenceType前の)すべてのタイプに対して実装された関数があり、次のものが含まれますArray

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

特定のケースでjoined()は、新しい配列の代わりに遅延コレクションを返すため、の使用が有益な場合がありArray()ますが、上記の例のように初期化子に渡されると、常に配列に変換できます。


@chrisco私の答えがどのように間違っているか、そして「最も単純な正しい答え」の基準は何ですか?回答を削除すると、質問にどのような影響があるのか​​教えていただけますか?
マックスDesiatov

まずスニペットを実行してみてください-何をしていると思いますか?それは実際に何をしますか?元の質問は何でしたか?あなたの答えは正しいですか?そうでない場合は、削除してこの投稿をわかりやすくすることをお勧めします。私は自分の間違った答えで同じことをしました。
Chris Conover、2015年

1
@chriscoは提案をありがとうございましたが、どこにでも投稿する前にスニペットを実行します。そして、私の答えは、OPが要求したものとまったく同じ結果を返し、そのために使用するコードが少ないため、正解です。私の元の答えは、配列ではなく遅延コレクションを返すことでしたが、質問には制限がありませんでした。正解を削除しても質問の質が向上することはないと思います。
Max Desiatov

これが私のポイントでした-出力をテスト/印刷すると、配列の配列が得られるということですFlattenBidirectionalCollection<Array<Array<Int>>>(_base: [[1, 2, 3], [4], [5, 6, 7, 8, 9]]))。フラットアレイのようにアクセスできるという点は有効ですが、CustomStringConvertable実装が誤解を招くように思われます。あなたのコードスニペットはテストされていませんでしたが、まだテストがありません。
Chris Conover、2015年

1
flatten()joined()
Swift

16

Swift 4.x / 5.x

配列をもう少し複雑にするために、配列の配列を含む配列がある場合、flatMap実際には失敗します。

配列が

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

flatMapcompactMapを返すです。

array.compactMap({$0})

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

この問題を解決するために、単純なforループロジック+再帰を使用できます。

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

以下に簡単な配列拡張を書きました。を使用して、別の配列または要素を含む配列をフラット化できます。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

説明

最初に初期化された累積値と、sequenceの各要素を順番組み合わせて結合を繰り返し呼び出した結果を返します。


あなたのコードで私は得る:Use of unresolved identifier 'reduce'
Jason Moore

1

@RahmiBozdagの回答を変更しました。1。パブリック拡張機能のメソッドはパブリックです。2.開始インデックスは常にゼロになるため、余分なメソッドを削除しました。3.内部メソッドTは常に[Any?]であるため、nilとオプションのCompactMapを内部に配置する方法が見つかりませんでした。提案は歓迎されます。

 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では、これを使用できます= Array(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)
}

ここに画像の説明を入力してください

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.