Create array of arrays by two properties

I have a Match object which contains a 'row' property and a 'column' property. I then have a Set<Match> with a bunch of those entities. I want to get a "matchesByColumnAndRow" variable that's an array of arrays. So the first element would be an array, sorted by 'row', containing the smallest value for column (In this case 'column' property can have negative numbers, which is why I say smallest, vs. 0)


I have this working via the following code, but I really don't like it. First it sorts into a flat array by column and then row, and then it creates the array of arrays. Technically it's not wrong, and I'm not so worried about performance, but I'm just wondering if there's a cleaner way to do this.



    private lazy var matchesByColumnAndRow: [[Match]] = {
        let sorted = self.matches.sort {
            switch ($0.0, $0.1) {
            case let (lhs, rhs) where lhs.column == rhs.column:
                return lhs.row < rhs.row
            case let (lhs, rhs):
                return lhs.column < rhs.column
            }
        }
        var byColumnAndRow: [[Match]] = []
        var cur: [Match] = []
        var prevColumn = Int.min
        for match in sorted {
            if match.column != prevColumn {
                if prevColumn != Int.min {
                    byColumnAndRow.append(cur)
                    cur = []
                }
                prevColumn = match.column
            }
            cur.append(match)
        }
        return byColumnAndRow
    }()
// Use this Group By collection extension from Alejandro Martinez' blog post "GroupBy in Swift 2"

extension CollectionType {
   
    public func groupBy(grouper: (Self.Generator.Element, Self.Generator.Element) -> Bool) -> [[Self.Generator.Element]] {
        var result : Array<Array<Self.Generator.Element>> = []
       
        var previousItem : Self.Generator.Element?
        var group : [Self.Generator.Element] = []
       
        for item in self {
            defer { previousItem = item }
           
            guard let previous = previousItem else {
                group.append(item)
                continue
            }
           
            if grouper(previous, item) { // item in the same group
                group.append(item)
            }
            else { // new group
                result.append(group)
                group = []
                group.append(item)
            }
        }
       
        result.append(group)
       
        return result
    }
}


import Foundation

struct Match : Equatable, Hashable, CustomStringConvertible {
    let id: NSUUID
    let row: Int
    let col: Int
    var hashValue: Int { return self.id.hashValue }


    init(row:Int, col: Int) {
        id = NSUUID()
        self.row = row
        self.col = col
    }
   
    var description: String {
        return "id: \(id.UUIDString), r: \(row), c: \(col)"
    }
}

func ==(lhs: Match, rhs: Match) -> Bool {
    return lhs.id == rhs.id
}


let m1 = Match(row: 10, col: 20)
let m2 = Match(row:100, col:200)

var data: Set<Match> = Set([m1, m2])
data.insert(Match(row: 500, col: 1))
data.insert(Match(row: 100, col: 201))
data.insert(Match(row: 1,   col: 2))
data.insert(Match(row: 1,   col: 42))
data.insert(Match(row: 1,   col: 66))
data.insert(Match(row: 10,  col: 20))
data.insert(Match(row: 42,  col: 1))
data.insert(Match(row: 2,   col: 42))
data.insert(Match(row: 1,   col: 42))


let sortedData: [Match] = Array(data).sort { // sort by Column and Row
    if $0.0.col != $0.1.col {
        return $0.0.col < $0.1.col
    }
    else {
        return $0.0.row < $0.1.row
    }
}


let groupedData: [[Match]] = sortedData.groupBy { $0.col == $1.col } // group by Column

print(groupedData) // 7 groups

/* [[id: CE0A3D9, r: 42,  c: 1,

     id: 160FE06, r: 500, c: 1],
    [id: B735A59, r: 1,   c: 2],
    [id: 7F2D4DA, r: 10,  c: 20,

     id: A18D550, r: 10,  c: 20],

    [id: E5FE090, r: 1,   c: 42,

     id: 7CBC8B1, r: 1,   c: 42,

     id: BC3624B, r: 2,   c: 42],

    [id: 89EA24B, r: 1,   c: 66],

    [id: 2B2D281, r: 100, c: 200],
 
    [id: F823D1C, r: 100, c: 201]] */

That's pretty similar to what I was already doing. I ended up finalizing on this, which will be easier to understand in the future.



        var prevColumn = sorted[0].column
        var startIndex = 0

        var byColumnAndRow: [[Match]] = []
        for (index, match) in sorted.enumerate() {
            guard prevColumn != match.column else { continue }

            let column = Array(sorted[startIndex ..< index])
            byColumnAndRow.append(column)

            startIndex = index
            prevColumn = match.column
        }
       
        // The previous enumeration won't grab the very last column worth of matches because the
        // loop ends before the column changes again.
        byColumnAndRow.append(Array(sorted[startIndex ..< sorted.count]))
Create array of arrays by two properties
 
 
Q