Compare commits

..

No commits in common. "main" and "0.1.0" have entirely different histories.
main ... 0.1.0

13 changed files with 25 additions and 229 deletions

View File

@ -25,7 +25,7 @@ jobs:
echo "<script>window.location.href += \"/documentation/levenshteintransformations\"</script><p>Please enable JavaScript to view the documentation <a href='/documentation/levenshteintransformations'>here</a>.</p>" > docs/index.html; echo "<script>window.location.href += \"/documentation/levenshteintransformations\"</script><p>Please enable JavaScript to view the documentation <a href='/documentation/levenshteintransformations'>here</a>.</p>" > docs/index.html;
sed -i '' 's/,2px/,10px/g' docs/css/index.*.css sed -i '' 's/,2px/,10px/g' docs/css/index.*.css
- name: Upload - name: Upload
uses: wangyucode/sftp-upload-action@v2.0.4 uses: wangyucode/sftp-upload-action@v2.0.2
with: with:
host: 'volans.uberspace.de' host: 'volans.uberspace.de'
username: 'akforum' username: 'akforum'

1
.gitignore vendored
View File

@ -11,4 +11,3 @@ DerivedData/
.swiftpm/config/registries.json .swiftpm/config/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc .netrc
/.vscode

View File

@ -1,16 +0,0 @@
cmake_minimum_required(VERSION 3.29)
cmake_policy(SET CMP0157 NEW)
project(LevenshteinTransformations
LANGUAGES Swift)
set(CMAKE_Swift_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/swift)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
add_subdirectory(Sources)
if(CMAKE_PROJECT_NAME STREQUAL "LevenshteinTransformations")
add_subdirectory(Tests)
endif()

View File

@ -23,18 +23,12 @@ let package = Package(
], ],
targets: [ targets: [
.target( .target(
name: "LevenshteinTransformations", name: "LevenshteinTransformations"
exclude: [
"CMakeLists.txt"
]
), ),
.executableTarget( .executableTarget(
name: "LevenshteinTransformationsTests", name: "LevenshteinTransformationsTests",
dependencies: ["LevenshteinTransformations"], dependencies: ["LevenshteinTransformations"],
path: "Tests", path: "Tests"
exclude: [
"CMakeLists.txt"
]
) )
] ]
) )

View File

@ -1 +0,0 @@
add_subdirectory(LevenshteinTransformations)

View File

@ -30,20 +30,4 @@ public struct AsyncFunctions<Element> {
self.insert = insert self.insert = insert
} }
/// Initialize a functions value.
/// - Parameters:
/// - delete: Delete the element at a certain index.
/// - insert: Insert a certain element at a certain index.
public init(
delete: @escaping (Int) async -> Void,
insert: @escaping (Int, Element) async -> Void
) {
self.replace = { index, element in
await delete(index)
await insert(index, element)
}
self.delete = delete
self.insert = insert
}
} }

View File

@ -1,15 +0,0 @@
add_library(LevenshteinTransformations
Extensions/Array.swift
Extensions/String.swift
AsyncFunctions.swift
EditOperation.swift
Functions.swift
LevenshteinTransformations.swift
Transformation.swift
)
install(TARGETS LevenshteinTransformations
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
)

View File

@ -11,14 +11,14 @@ extension Array where Element: Equatable {
/// - Parameter target: The target array. /// - Parameter target: The target array.
/// - Returns: The Levenshtein distance. /// - Returns: The Levenshtein distance.
public func levenshteinDistance(to target: [Element]) -> Int { public func levenshteinDistance(to target: [Element]) -> Int {
levenshteinDistance(to: target, id: \.self) getTransformations(to: target).count
} }
/// Get the transformations needed to transform the array into the target array. /// Get the transformations needed to transform the array into the target array.
/// - Parameter target: The target array. /// - Parameter target: The target array.
/// - Returns: The transformations. /// - Returns: The transformations.
public func getTransformations(to target: [Element]) -> [Transformation<Element>] { public func getTransformations(to target: [Element]) -> [Transformation<Element>] {
getTransformations(to: target, id: \.self) LevenshteinTransformations.levenshteinTransformations(from: self, to: target)
} }
/// Call every transformation step needed to transform the array into the target array. /// Call every transformation step needed to transform the array into the target array.
@ -26,7 +26,12 @@ extension Array where Element: Equatable {
/// - target: The target array. /// - target: The target array.
/// - functions: The transformation functions. /// - functions: The transformation functions.
public func transform(to target: [Element], functions: Functions<Element>) { public func transform(to target: [Element], functions: Functions<Element>) {
transform(to: target, id: \.self, functions: functions) var transformations = getTransformations(to: target)
while !transformations.isEmpty {
let transformation = transformations.removeFirst()
transformation.transform(functions: functions, nextTransformations: &transformations)
}
} }
/// Call every transformation step needed to transform the array into the target array. /// Call every transformation step needed to transform the array into the target array.
@ -34,7 +39,12 @@ extension Array where Element: Equatable {
/// - target: The target array. /// - target: The target array.
/// - functions: The transformation functions. /// - functions: The transformation functions.
public func transform(to target: [Element], functions: AsyncFunctions<Element>) async { public func transform(to target: [Element], functions: AsyncFunctions<Element>) async {
await transform(to: target, id: \.self, functions: functions) var transformations = getTransformations(to: target)
while !transformations.isEmpty {
let transformation = transformations.removeFirst()
await transformation.transform(functions: functions, nextTransformations: &transformations)
}
} }
} }
@ -60,71 +70,7 @@ extension Array where Element: Identifiable {
/// - target: The target array. /// - target: The target array.
/// - functions: The transformation functions. /// - functions: The transformation functions.
public func identifiableTransform(to target: [Element], functions: Functions<Element>) { public func identifiableTransform(to target: [Element], functions: Functions<Element>) {
transform(to: target, id: \.id, functions: functions) var transformations = identifiableGetTransformations(to: target)
}
/// Call every transformation step needed to transform the array into the target array.
/// - Parameters:
/// - target: The target array.
/// - functions: The transformation functions.
public func identifiableTransform(to target: [Element], functions: AsyncFunctions<Element>) async {
await transform(to: target, id: \.id, functions: functions)
}
}
extension Array {
/// Calculate the Levenshtein distance to another array.
/// - Parameters:
/// - target: The target array.
/// - id: The identifier.
/// - Returns: The Levenshtein distance.
public func levenshteinDistance<Identifier>(
to target: [Element],
id: KeyPath<Element, Identifier>
) -> Int where Identifier: Equatable {
getRawTransformations(to: target, id: id).count
}
/// Get the transformations needed to transform the array into the target array with the raw identifier data.
/// - Parameters:
/// - target: The target array.
/// - id: The identifier.
/// - Returns: The transformations applied to the identifier.
public func getRawTransformations<Identifier>(
to target: [Element],
id: KeyPath<Element, Identifier>
) -> [Transformation<Identifier>] where Identifier: Equatable {
let equatableSource = map { $0[keyPath: id] }
let equatableTarget = target.map { $0[keyPath: id] }
return LevenshteinTransformations.levenshteinTransformations(from: equatableSource, to: equatableTarget)
}
/// Get the transformations needed to transform the array into the target array.
/// - Parameters:
/// - target: The target array.
/// - id: The identifier.
/// - Returns: The transformations.
public func getTransformations<Identifier>(
to target: [Element],
id: KeyPath<Element, Identifier>
) -> [Transformation<Element>] where Identifier: Equatable {
getRawTransformations(to: target, id: id)
.compactMap { $0.convert { targetID in target.first { $0[keyPath: id] == targetID } } }
}
/// Call every transformation step needed to transform the array into the target array.
/// - Parameters:
/// - target: The target array.
/// - id: The identifier key path.
/// - functions: The transformation functions.
public func transform<Identifier>(
to target: [Element],
id: KeyPath<Element, Identifier>,
functions: Functions<Element>
) where Identifier: Equatable {
var transformations = getTransformations(to: target, id: id)
while !transformations.isEmpty { while !transformations.isEmpty {
let transformation = transformations.removeFirst() let transformation = transformations.removeFirst()
@ -135,14 +81,9 @@ extension Array {
/// Call every transformation step needed to transform the array into the target array. /// Call every transformation step needed to transform the array into the target array.
/// - Parameters: /// - Parameters:
/// - target: The target array. /// - target: The target array.
/// - id: The identifier key path.
/// - functions: The transformation functions. /// - functions: The transformation functions.
public func transform<Identifier>( public func identifiableTransform(to target: [Element], functions: AsyncFunctions<Element>) async {
to target: [Element], var transformations = identifiableGetTransformations(to: target)
id: KeyPath<Element, Identifier>,
functions: AsyncFunctions<Element>
) async where Identifier: Equatable {
var transformations = getTransformations(to: target, id: id)
while !transformations.isEmpty { while !transformations.isEmpty {
let transformation = transformations.removeFirst() let transformation = transformations.removeFirst()

View File

@ -30,20 +30,4 @@ public struct Functions<Element> {
self.insert = insert self.insert = insert
} }
/// Initialize a functions value.
/// - Parameters:
/// - delete: Delete the element at a certain index.
/// - insert: Insert a certain element at a certain index.
public init(
delete: @escaping (Int) -> Void,
insert: @escaping (Int, Element) -> Void
) {
self.replace = { index, element in
delete(index)
insert(index, element)
}
self.delete = delete
self.insert = insert
}
} }

View File

@ -20,17 +20,17 @@ let target = [0, 1, 5, 6, 10]
There are three functions available for arrays containing elements conforming to `Equatable` and strings. There are three functions available for arrays containing elements conforming to `Equatable` and strings.
### Arrays and Strings ### Arrays with Equatable Elements and Strings
#### Levenshtein Distance #### Levenshtein Distance
Get the Levenshtein distance with ``Swift/Array/levenshteinDistance(to:id:)`` (for strings: ``Swift/String/levenshteinDistance(to:)``). Get the Levenshtein distance with ``Swift/Array/levenshteinDistance(to:)`` (for strings: ``Swift/String/levenshteinDistance(to:)``).
```swift ```swift
print(source.levenshteinDistance(to: target)) print(source.levenshteinDistance(to: target))
``` ```
This example outputs `3`. This example outputs `3`.
#### Transformation Steps #### Transformation Steps
Get the individual transformation steps with ``Swift/Array/getTransformations(to:id:)`` (for strings: ``Swift/String/getTransformations(to:)``). Get the individual transformation steps with ``Swift/Array/getTransformations(to:)`` (for strings: ``Swift/String/getTransformations(to:)``).
```swift ```swift
print(source.getTransformations(to: target).map { $0.description(source: source) }.joined(separator: "\n")) print(source.getTransformations(to: target).map { $0.description(source: source) }.joined(separator: "\n"))
``` ```
@ -42,7 +42,7 @@ Insert '10' at position 4
``` ```
#### Run Functions for the Steps #### Run Functions for the Steps
Directly run functions for the `replace`, `delete` and `insert` transformations using the ``Swift/Array/transform(to:id:functions:)-5sa4j`` (for strings: ``Swift/String/transform(to:id:functions:)-3b6nc``) function. Directly run functions for the `replace`, `delete` and `insert` transformations using the ``Swift/Array/transform(to:functions:)-2wzm8`` (for strings: ``Swift/String/transform(to:functions:)-3b6nc``) function.
```swift ```swift
var modified = source var modified = source
@ -61,7 +61,7 @@ print(modified == target)
``` ```
This example outputs `true`. This example outputs `true`.
The transform functions are available in an asynchronous version: ``Swift/Array/transform(to:id:functions:)-1gw5x`` for arrays and ``Swift/String/transform(to:functions:)-46is`` for strings. The transform functions are available in an asynchronous version: ``Swift/Array/transform(to:functions:)-756uw`` for arrays and ``Swift/String/transform(to:functions:)-46is`` for strings.
### Arrays with Identifiable Elements ### Arrays with Identifiable Elements

View File

@ -61,29 +61,6 @@ public enum Transformation<Element> {
} }
} }
/// Get the replace transformation for an element, or nil.
/// - Parameters:
/// - index: The index to replace.
/// - element: The new element.
public static func replace(at index: Int, with element: Element?) -> Self? {
guard let element else {
return nil
}
return .replace(at: index, with: element)
}
/// Get the insert transformation for an element, or nil.
/// - Parameters:
/// - index: The index to insert at.
/// - element. The new element.
/// - Returns: The transformation.
public static func insert(at index: Int, element: Element?) -> Self? {
guard let element else {
return nil
}
return .insert(at: index, element: element)
}
/// A description of the transformation. /// A description of the transformation.
/// - Parameter source: The initial array. /// - Parameter source: The initial array.
/// - Returns: The description. /// - Returns: The description.
@ -149,21 +126,6 @@ public enum Transformation<Element> {
} }
} }
/// Convert a series of transformations in order to be able
/// to apply the same transformation on different Swift types.
/// - Parameter convert: The conversion function.
/// - Returns: The transformations.
public func convert<NewType>(convert: (Element) -> NewType?) -> Transformation<NewType>? {
switch self {
case let .replace(index, element):
return .replace(at: index, with: convert(element))
case let .delete(index):
return .delete(at: index)
case let .insert(index, element):
return .insert(at: index, element: convert(element))
}
}
} }
// swiftlint:enable identifier_name // swiftlint:enable identifier_name

View File

@ -1,11 +0,0 @@
add_executable(Tests
LevenshteinTransformationsTests.swift
)
target_compile_options(Tests PUBLIC
-parse-as-library
)
target_link_libraries(Tests PRIVATE
LevenshteinTransformations
)

View File

@ -21,30 +21,6 @@ enum LevenshteinTransformationsTests {
} }
/// Test the transform function with a custom path.
static func testCustom() {
let source = [1, 2, 5, 6].map { TestType(id: $0) }
let target = [0, 1, 5, 6, 10].map { TestType(id: $0) }
var modified = source
source.transform(
to: target,
id: \.id,
functions: .init { index, element in
modified[index] = element
} delete: { index in
modified.remove(at: index)
} insert: { index, element in
modified.insert(element, at: index)
}
)
print(source.getRawTransformations(to: target, id: \.id))
print(modified == target)
}
/// Test the `transform` function on an equatable array. /// Test the `transform` function on an equatable array.
static func testEquatable() { static func testEquatable() {
let source = [1, 2, 5, 6] let source = [1, 2, 5, 6]
@ -110,7 +86,6 @@ enum LevenshteinTransformationsTests {
/// Run the tests. /// Run the tests.
static func main() { static func main() {
testCustom()
testEquatable() testEquatable()
testIdentifiable() testIdentifiable()
testString() testString()