I'd like to apply some kind of special pattern find algorithm using Swift.
Some explanations:
I'm getting a simple 1-dimensional array that could look like this:
var array = [
"0000000000000000000",
"0000001110000000000",
"0000011111000000000",
"0000001110000000000",
"0000000000000000000",
"0001100000000000000",
"0001100000000011000",
"0011100000000011000",
"0000000000000000000"
]
And I'd like to extract the connected areas of "1"-characters (connected components).
Have a look at this:
111
11111
111
11
11 11
111 11
I'd like to get as result a multidimensional array that includes all x/y-positions of the single components.
var result = [
[ [6,1], [7,1], [8,1], [5,2], [6,2], [7,2], [8,2], [9,2], [6,3], [7,3], [8,2] ] // positions of the first area (the biggest one on top)
[ [3,5], [4,5], [3,6], [4,6], [2,7], [3,7], [4,7] ] // area bottom left
[ [14,6], [15,6], [14,7], [15,7] ] // area bottom right (smallest area)
]
I've coded the function for javascript. You can find the code right here:
var matrix = [ "0000000000000000000", "0000001110000000000", "0000011111000000000", "0000001110000000000", "0000000000000000000", "0001100000000000000", "0001100000000011000", "0011100000000011000", "0000000000000000000" ] Array.prototype.extract_components_positions = function(offset) { var array = this.map(item => item.split('')).map(str => Array.from(str, Number)), default_value = 0, result_object = {} function test_connection(array, i, j) { if (array[i] && array[i][j] === -1) { if (!result_object[default_value]) result_object[default_value] = []; result_object[default_value].push([j, i]); array[i][j] = 1; for (var k = offset; k > 0; k--) { test_connection(array, i + k, j); // left - right test_connection(array, i, j + k); // top - bottom test_connection(array, i - k, j); // right - left test_connection(array, i, j - k); // bottom - top } return true } } array.forEach(function(a) { a.forEach(function(b, i, bb) { bb[i] = -b }) }); array.forEach(function(a, i, aa) { a.forEach(function(b, j, bb) { test_connection(aa, i, j) && default_value++ }) }) return [result_object]; } var result = matrix.extract_components_positions(1); console.log(JSON.stringify(result))
but I have a big problems translating this Javascript code into Swift!
func extract_components_positions(matrix: [[String]],offset: Int) {
var array = [[]] // no idea how to use map to split the array from ["0011100"],... to ["0","0","1","1",...], ...
var default_value = 0,
result_object = [[Int]()]
func testconnection(matrix: [[String]], i: Int, j: Int) -> [[Int]] {
if (Int(array[i][j] as! Int) == -1) {
array[i][j] = 1
for var k in offset...0 {
testconnection(matrix: array, i: i+k, j: j) // error: "Cannot convert value of type '[[Any]]' to expected argument type '[[String]]'"
testconnection(matrix: array, i: i, j: j+k)
testconnection(matrix: array, i: i-k, j: j)
testconnection(matrix: array, i: i, j: j-k)
}
}
}
array.forEach { (a) in
a.forEach({ (b, i, bb) in // error: "Contextual closure type '(Any) -> Void' expects 1 argument, but 3 were used in closure body"
bb[i] = -b
})
}
array.forEach { (a, i, aa) in // error: "Contextual closure type '([Any]) -> Void' expects 1 argument, but 3 were used in closure body"
a.forEach({ (b, j, bb) in
testconnection(aa, i, j) && default_value++
})
}
return result_object
}
Any help how to fix my code would be very appreciated.
Look like you are playing Minesweeper! Here's my solution (in swift 4.0, Xcode 9.2). See inline comments for explanation.
let array = [
"0000000000000000000",
"0000001110000000000",
"0000011111000000000",
"0000001110000000000",
"0000000000000000000",
"0001100000000000000",
"0001100000000011000",
"0011100000000011000",
"0000000000000000000"
]
// A structure to hold the cell's coordinate as Int array
// can become confusing very quickly
struct Cell: Equatable {
var row: Int
var column: Int
var clusterIndex: Int?
static func == (lhs: Cell, rhs: Cell) -> Bool {
return lhs.row == rhs.row && lhs.column == rhs.column
}
}
// Get all the "1" cells
var cells = array.enumerated().flatMap { arg -> [Cell] in
let (rowIndex, str) = arg
// The flatMap below will become compactMap in Swift 4.1
return str.enumerated().flatMap { colIndex, char in
if char == "1" {
return Cell(row: rowIndex, column: colIndex, clusterIndex: nil)
} else {
return nil
}
}
}
// Assign each cell a clusterIndex
for (i, currentCell) in cells.enumerated() {
// A cell may not have all four neighbors, or not all its
// neighbors are "1" cells, hence the "potential"
let potentialNeighbors = [
Cell(row: currentCell.row - 1, column: currentCell.column, clusterIndex: nil), // above
Cell(row: currentCell.row + 1, column: currentCell.column, clusterIndex: nil), // below
Cell(row: currentCell.row, column: currentCell.column - 1, clusterIndex: nil), // left
Cell(row: currentCell.row, column: currentCell.column + 1, clusterIndex: nil) // right
]
// Get the actual neighboring cells and their indexes
let neighborsAndIndexes = cells.enumerated().filter { arg in
let (_, c) = arg
return potentialNeighbors.contains(c)
}
let neighborIndexes = neighborsAndIndexes.map { $0.0 }
let neighbors = neighborsAndIndexes.map { $0.1 }
// Determine what clusterIndex we should give the current cell and its neighbors
var clusterIndex = 0
if currentCell.clusterIndex != nil {
// If the current cell already has a clusteredIndex, reuse it
clusterIndex = currentCell.clusterIndex!
} else if let neighborClusterIndex = neighbors.first(where: { $0.clusterIndex != nil })?.clusterIndex {
// If the current cell has a neighbor whose clusterIndex is not nil, use that
clusterIndex = neighborClusterIndex
} else {
// Else increment from the max existing clusterIndex
clusterIndex = (cells.map({ $0.clusterIndex ?? 0 }).max() ?? 0) + 1
}
// Assign the same clusterIndex to the current cell and its neighbors
([i] + neighborIndexes).forEach {
cells[$0].clusterIndex = clusterIndex
}
}
// Group the cells by their clusterIndex
let clusters = Dictionary(grouping: cells, by: { $0.clusterIndex! })
.sorted(by: { $0.key < $1.key })
.map { $0.value }
// Print the result
// Visualize which cell belong to which cluster and how it appears on the board
for i in 0..<array.count {
for j in 0..<array[0].count {
if let clusterIndex = cells.first(where: { $0.row == i && $0.column == j })?.clusterIndex {
print(clusterIndex, terminator: "")
} else {
print("-", terminator: "")
}
}
print() // print a newline
}
Result:
-------------------
------111----------
-----11111---------
------111----------
-------------------
---22--------------
---22---------33---
--222---------33---
-------------------
Note that in Swift 4.1 (currently in beta), the flatMap
we use here has been renamed to compactMap
. This is not to say that flatMap
is going away completely. flatMap
has 3 versions, only 1 of them has been renamed to compactMap
. For more info, see SE-0187 .
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.