Skip to content

Commit

Permalink
Added FeatureCollection.propertiesSummary() (#63)
Browse files Browse the repository at this point in the history
  • Loading branch information
trasch committed Jul 30, 2024
1 parent 32b933f commit 98feada
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 3 deletions.
25 changes: 23 additions & 2 deletions Sources/GISTools/Algorithms/EnumerateProperties.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,34 @@ import Foundation

extension FeatureCollection {

/// Enumerate all properties in the FeatureCollection with feature index..
/// Enumerate over all Feature properties in the FeatureCollection.
///
/// - Parameter callback: The callback function
/// - Parameter callback: The callback function, called for each Feature with Feature index and properties
public func enumerateProperties(_ callback: (_ featureIndex: Int, _ properties: [String: Sendable]) -> Void) {
for (featureIndex, feature) in features.enumerated() {
callback(featureIndex, feature.properties)
}
}

/// Creates a summary over all properties in the FeatureCollection.
///
/// - Returns: A dictionary with all the keys found in all properties of the FeatureCollection,
/// and the values are the distinct values for each key.
///
/// - Note: All valid JSON types are `Hashable`.
public func propertiesSummary() -> [String: [AnyHashable]] {
var keyValuePairs: [(String, Set<AnyHashable>)] = []

for feature in features {
for (key, value) in feature.properties {
if let hashable = value as? AnyHashable {
keyValuePairs.append((key, Set([hashable])))
}
}
}

return Dictionary(keyValuePairs, uniquingKeysWith: { $0.union($1) })
.mapValues(\.asArray)
}

}
12 changes: 12 additions & 0 deletions Sources/GISTools/Extensions/SetExtensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Foundation

// MARK: Private

extension Set {

/// Converts the Set to an Array
var asArray: [Element] {
Array(self)
}

}
35 changes: 34 additions & 1 deletion Tests/GISToolsTests/GeoJson/FeatureCollectionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ final class FeatureCollectionTests: XCTestCase {
(2, 4, Coordinate3D(latitude: 0.0, longitude: 100.0))
]

var result:[(Int, Int, Coordinate3D)] = []
var result: [(Int, Int, Coordinate3D)] = []
featureCollection.enumerateCoordinates { featureIndex, coordinateIndex, coordinate in
result.append((featureIndex, coordinateIndex, coordinate))
}
Expand All @@ -164,6 +164,39 @@ final class FeatureCollectionTests: XCTestCase {
}
}

func testEnumerateProperties() throws {
let featureCollection = try XCTUnwrap(FeatureCollection(jsonString: FeatureCollectionTests.featureCollectionJson))

let expected: [(Int, [String: Sendable])] = [
(0, ["prop0": "value0"]),
(1, ["prop0": "value0", "prop1": 0]),
(2, ["prop0": "value0", "prop1": ["this": "that"]])
]

var result: [(Int, [String: Sendable])] = []
featureCollection.enumerateProperties { featureIndex, properties in
XCTAssertFalse(properties.isEmpty)
result.append((featureIndex, properties))
}

XCTAssertEqual(result.count, expected.count)

for (lhs, rhs) in zip(result, expected) {
XCTAssertEqual(lhs.0, rhs.0)
}
}

func testPropertiesSummary() throws {
let featureCollection = try XCTUnwrap(FeatureCollection(jsonString: FeatureCollectionTests.featureCollectionJson))

let summary = featureCollection.propertiesSummary()
XCTAssertEqual(summary.count, 2)
XCTAssertEqual(summary.keys.sorted(), ["prop0", "prop1"])
XCTAssertEqual(summary["prop0"], ["value0"])
XCTAssertTrue(summary["prop1"]!.contains(0))
XCTAssertTrue(summary["prop1"]!.contains(["this": "that"]))
}

func testEncodable() throws {
let featureCollection = try XCTUnwrap(FeatureCollection(jsonString: FeatureCollectionTests.featureCollectionJson))

Expand Down

0 comments on commit 98feada

Please sign in to comment.