Làm phẳng một mảng các mảng trong Swift


142

Có đối tác trong Swift to flattentrong Scala, Xtend, Groovy, Ruby và co không?

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

tất nhiên tôi có thể sử dụng giảm cho điều đó nhưng điều đó thật tệ

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

không phải nó giống như sử dụng thêm đối tượng của một mảng sao?
Phạm Hoàn

Tôi chưa nhìn vào Swift nhưng trong Haskell và F # đó là 'concat` - vì vậy có lẽ trông giống như thế này? - Tôi khá tích cực rằng đây là một nơi nào đó (hầu hết các lang FP. Biết về các đơn vị và đây là ràng buộc của List)
Carsten

vâng trong haskell nó thực sự được gọi là concat.
Christian Dietrich

Bạn nên chấp nhận câu trả lời của andreschneider .
Cướp

Câu trả lời:


435

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

nhập mô tả hình ảnh ở đây


3
Chỉ cần nói rõ điều này, flatMapcó sẵn kể từ Swift 1.2.
Mick MacCallum

3
sự khác biệt giữa joined(chính thức được gọi là flatten) với flatMap? Có phải là trong khi flatMaptham gia, nó cũng có thể ánh xạ / biến đổi mọi thứ. nhưng ở đây trong ví dụ chúng tôi thực sự không cần, tức là chúng tôi trở lại$0
Honey

6
@Dschee flatMapsẽ hoặc là flatten một mảng 2D thành một mảng 1D hoặc loại bỏ nilcác giá trị, không phải cả hai. Nó xác định việc cần làm dựa trên việc mảng của cấp 1 Elementlà mảng hay tùy chọn, do đó, nếu bạn chuyển cho nó một mảng tùy chọn 2D (ví dụ [[Int?]]), nó sẽ chọn làm phẳng nó thành 1D (ví dụ [Int?]) . Để làm phẳng cả 1-D và loại bỏ nils cấp 2, bạn phải làm array.flatMap { $0 }.flatMap { $0 }. Nói cách khác, việc làm phẳng kích thước là tương đương Array(array.joined())và việc loại bỏ nil không phải là phẳng array.filter{ $0 != nil }.map{ $0! }.
Slipp D. Thompson

1
@Warpling flatMapvẫn thích hợp cho việc sử dụng được mô tả trong câu hỏi (làm phẳng một mảng 2D thành 1D). compactMaprõ ràng là để loại bỏ nilcác mục từ một chuỗi, như một biến thể của flatMapmột lần đã làm.
Jim Dovey

1
@mohamadrezakoohkan đúng vậy. Vì mảng của bạn là loại [[Any]], nên flatMapchỉ cần chuyển đổi nó thành một loại [Any]([1, 2, 3, 4, [5, 6], 7, 8, 9]). Và nếu chúng tôi áp dụng flatMaplại, chúng tôi sẽ hành động theo một 'Bất kỳ? loại, trong đó trình biên dịch không biết nữa nếu nó là một giá trị đơn giản hoặc chính một mảng.
andreschneider

31

Trong thư viện chuẩn Swift có joinedchức năng được triển khai cho tất cả các loại tuân thủ Sequencegiao thức (hoặc flattenbật SequenceTypetrước Swift 3), bao gồm Array:

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

Trong một số trường hợp nhất định, việc sử dụng joined()có thể có lợi vì nó trả về một bộ sưu tập lười biếng thay vì một mảng mới, nhưng luôn có thể được chuyển đổi thành một mảng khi được chuyển sang trình Array()khởi tạo như trong ví dụ trên.


@chrisco bạn có thể vui lòng giải thích cách trả lời của tôi không đúng và tiêu chí cho "câu trả lời đúng đơn giản nhất" là gì? Bạn có thể vui lòng cho biết cách xóa một câu trả lời có thể ảnh hưởng đến câu hỏi theo bất kỳ cách nào không?
Max Desiatov

Hãy thử chạy đoạn trích của bạn trước - bạn nghĩ nó làm gì? Nó thực sự làm gì? Câu hỏi ban đầu là gì? Câu trả lời của bạn có đúng không? Nếu không, tốt hơn là xóa nó để cải thiện sự rõ ràng của bài đăng này. Tôi đã làm tương tự với câu trả lời không chính xác của riêng tôi.
Chris Conover

1
@chrisco cảm ơn bạn rất nhiều vì những gợi ý của bạn, nhưng tôi vẫn chạy đoạn trích trước khi đăng chúng ở bất cứ đâu. Và câu trả lời của tôi là chính xác vì nó trả về kết quả chính xác như OP yêu cầu và sử dụng ít mã hơn cho điều đó. Tôi thừa nhận rằng câu trả lời ban đầu của tôi là trả về bộ sưu tập lười biếng thay vì một mảng, mặc dù không có hạn chế nào trong câu hỏi đó. Tôi vẫn không nghĩ rằng việc xóa một câu trả lời đúng sẽ cải thiện chất lượng câu hỏi theo bất kỳ cách nào.
Max Desiatov

Đây là quan điểm của tôi - rằng khi kiểm tra / in đầu ra, bạn sẽ nhận được một mảng các mảng : FlattenBidirectionalCollection<Array<Array<Int>>>(_base: [[1, 2, 3], [4], [5, 6, 7, 8, 9]])). Quan điểm của bạn là hợp lệ mặc dù bạn có thể truy cập nó như một mảng phẳng, do đó có vẻ như việc CustomStringConvertabletriển khai là sai lệch. Đoạn mã của bạn đã và vẫn đang thiếu một bài kiểm tra.
Chris Conover

1
Kể từ swift 3.0, flatten()đã được đổi tên thànhjoined()
Ông Xcoder

16

Swift 4.x / 5.x

Chỉ cần thêm một chút phức tạp vào mảng, nếu có một mảng chứa mảng mảng, thì flatMapthực sự sẽ thất bại.

Giả sử mảng là

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

Những gì flatMaphoặc compactMaptrả lại là:

array.compactMap({$0})

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

Để giải quyết vấn đề này, chúng ta có thể sử dụng đơn giản cho logic vòng lặp + đệ quy

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
}

Vì vậy, gọi hàm này với mảng đã cho

flattenedArray(array: array)

Kết quả là:

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

Hàm này sẽ giúp làm phẳng bất kỳ loại mảng nào, xem xét trường hợp Intở đây

Đầu ra sân chơi: nhập mô tả hình ảnh ở đây




2

Swift 4.2

Tôi đã viết một phần mở rộng mảng đơn giản dưới đây. Bạn có thể sử dụng để làm phẳng một mảng có chứa một mảng hoặc phần tử khác. không giống như phương thức tham gia ().

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)
    }
}

sử dụng:

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

numbers.flatten()

1

Một cách thực hiện chung chung khác của reduce,

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

Điều này hoàn thành điều tương tự nhưng có thể cung cấp cái nhìn sâu sắc hơn về những gì đang diễn ra reduce .

Từ tài liệu của Apple,

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

Sự miêu tả

Trả về kết quả của việc gọi liên tục kết hợp với một giá trị tích lũy được khởi tạo thành ban đầu và lần lượt từng phần tử của chuỗi .


Với mã của bạn, tôi nhận được:Use of unresolved identifier 'reduce'
Jason Moore

1

Đã sửa đổi câu trả lời của @ RahmiBozdag, 1. Các phương thức trong tiện ích mở rộng công khai. 2. Loại bỏ phương thức bổ sung, vì chỉ số bắt đầu sẽ luôn bằng không. 3. Tôi không tìm thấy cách đặt compactMap bên trong cho nil và tùy chọn vì bên trong phương thức T luôn là [Bất kỳ?], Bất kỳ đề xuất nào đều được hoan nghênh.

 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

Bạn có thể làm phẳng mảng lồng nhau bằng phương pháp sau:

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 phiên bản 5.1.2 (swiftlang-1100.0.278 clang-1100.0.33.9)
Mục tiêu: x86_64-apple-darwin19.2.0

Ảnh chụp màn hình

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([], +)
    }
}

Trong trường hợp bạn cũng muốn nó cho các giá trị từ điển:

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


-1

ma trận là [[myDTO]]?

Trong swift 5, bạn có thể sử dụng this = 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)
}

nhập mô tả hình ảnh ở đây

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.