Top Related Projects
MIT Deep Learning Book in PDF format (complete and parts) by Ian Goodfellow, Yoshua Bengio and Aaron Courville
Commonly used data structures for Swift
Epoxy is a suite of declarative UI APIs for building UIKit applications in Swift
💻 A fast and flexible O(n) difference algorithm framework for Swift collection.
A data-driven UICollectionView framework for building fast and flexible lists.
Quick Overview
DeepDiff is a Swift library that provides a powerful and efficient way to compare and diff two objects, arrays, or dictionaries. It can detect and report changes, insertions, deletions, and moves between the compared data structures.
Pros
- Comprehensive Diffing: DeepDiff can detect a wide range of changes, including insertions, deletions, updates, and moves, making it a versatile tool for comparing complex data structures.
- Efficient Performance: The library is designed to be efficient, with a focus on minimizing the time and memory required for diffing operations.
- Customizable Comparison: DeepDiff allows for customizable comparison functions, enabling users to define their own comparison logic for specific data types.
- Swift-Friendly: As a Swift library, DeepDiff integrates seamlessly with Swift projects and leverages the language's features, such as generics and protocol-oriented programming.
Cons
- Limited to Swift: DeepDiff is a Swift-specific library, which means it may not be directly usable in projects that don't use Swift.
- Potential Learning Curve: While the library is well-documented, users may need to invest some time to fully understand its capabilities and how to use it effectively in their projects.
- Dependency Management: Depending on the project's setup, integrating DeepDiff may require additional dependency management steps, which could add complexity to the setup process.
- Potential Performance Overhead: While DeepDiff is designed to be efficient, the diffing process may still introduce some performance overhead, especially for large or complex data structures.
Code Examples
Here are a few code examples demonstrating the usage of DeepDiff:
- Comparing Two Dictionaries:
let old: [String: Int] = ["a": 1, "b": 2, "c": 3]
let new: [String: Int] = ["a": 1, "b": 3, "d": 4]
let diff = old.diff(from: new)
print(diff) // Prints: [.update("b", 2, 3), .insert("d", nil, 4)]
- Comparing Two Arrays:
let old = [1, 2, 3, 4, 5]
let new = [2, 3, 4, 6, 7]
let diff = old.diff(from: new)
print(diff) // Prints: [.delete(0, 1), .insert(4, nil, 6), .insert(5, nil, 7)]
- Customizing the Comparison Logic:
struct Person: Equatable {
let name: String
let age: Int
}
let old = [Person(name: "Alice", age: 30), Person(name: "Bob", age: 35)]
let new = [Person(name: "Alice", age: 31), Person(name: "Charlie", age: 40)]
let diff = old.diff(from: new, by: { $0.name == $1.name })
print(diff) // Prints: [.update(1, Person(name: "Bob", age: 35), Person(name: "Charlie", age: 40))]
- Detecting Moves:
let old = [1, 2, 3, 4, 5]
let new = [3, 1, 4, 5, 2]
let diff = old.diff(from: new)
print(diff) // Prints: [.move(1, 0, 2), .move(4, 4, 1)]
Getting Started
To get started with DeepDiff, you can follow these steps:
-
Add the DeepDiff library to your project using your preferred dependency management tool, such as CocoaPods, Carthage, or Swift Package Manager.
-
Import the DeepDiff library in your Swift file:
import DeepDiff
- Use the
diff(from:)
method to compare two objects, arrays, or dictionaries and get the resulting differences:
let old = [1, 2, 3, 4, 5]
let new =
Competitor Comparisons
MIT Deep Learning Book in PDF format (complete and parts) by Ian Goodfellow, Yoshua Bengio and Aaron Courville
Pros of mit-deep-learning-book-pdf
- Provides a comprehensive resource for learning deep learning concepts
- Offers free access to a valuable textbook in PDF format
- Includes supplementary materials like lecture slides and exercises
Cons of mit-deep-learning-book-pdf
- Not an active software project; primarily a static resource
- Limited practical implementation examples compared to DeepDiff
- May require additional resources for hands-on learning
Code Comparison
While a direct code comparison is not relevant due to the nature of these repositories, we can highlight the difference in their purposes:
mit-deep-learning-book-pdf:
No code implementation; primarily contains PDF files and supplementary materials
DeepDiff:
let changes = diff(old: oldArray, new: newArray)
for change in changes {
switch change {
case .insert(let index, let element):
print("Insert \(element) at index \(index)")
// ... other cases
}
}
DeepDiff is an active Swift library for performing difference calculations, while mit-deep-learning-book-pdf is a collection of educational materials without code implementation.
Commonly used data structures for Swift
Pros of swift-collections
- Comprehensive collection of data structures, offering a wider range of tools for Swift developers
- Officially maintained by Apple, ensuring long-term support and compatibility with Swift updates
- Highly optimized for performance, with careful consideration of memory usage and efficiency
Cons of swift-collections
- Larger and more complex library, potentially increasing app size and compilation time
- Steeper learning curve due to the variety of data structures and their specific use cases
- May be overkill for projects that only need simple diffing functionality
Code Comparison
DeepDiff:
let changes = diff(old: oldItems, new: newItems)
tableView.reload(changes: changes, updateData: {
self.items = newItems
})
swift-collections:
var orderedSet = OrderedSet<String>()
orderedSet.append("Apple")
orderedSet.append("Banana")
orderedSet.remove("Apple")
Summary
DeepDiff is a focused library for calculating differences between collections, ideal for simple UI updates. swift-collections provides a broader set of data structures, offering more versatility but with added complexity. Choose DeepDiff for straightforward diffing needs, and swift-collections for more comprehensive data structure requirements in larger projects.
Epoxy is a suite of declarative UI APIs for building UIKit applications in Swift
Pros of Epoxy-iOS
- More comprehensive UI framework for building complex, dynamic interfaces
- Supports declarative UI programming with a focus on composition
- Offers advanced features like automatic diffing and batch updates
Cons of Epoxy-iOS
- Steeper learning curve due to its more extensive API and concepts
- Potentially overkill for simpler UI requirements
- Larger codebase and potential overhead compared to lightweight solutions
Code Comparison
DeepDiff:
let changes = diff(old: oldItems, new: newItems)
tableView.reload(changes: changes, updateData: {
self.items = newItems
})
Epoxy-iOS:
dataSource.setSections {
Section("header") {
for item in items {
ItemModel(item)
}
}
}
Summary
DeepDiff is a lightweight library focused specifically on efficient diffing and updating of collections. It's simpler to integrate and use for basic diffing needs. Epoxy-iOS, on the other hand, is a more comprehensive UI framework that includes diffing capabilities along with a declarative approach to building complex, dynamic interfaces. While Epoxy-iOS offers more advanced features and scalability, it may be excessive for simpler use cases where DeepDiff's straightforward approach could suffice.
💻 A fast and flexible O(n) difference algorithm framework for Swift collection.
Pros of DifferenceKit
- Higher performance for large datasets due to optimized diffing algorithm
- Supports sectioned data, making it suitable for complex table views
- More comprehensive documentation and examples
Cons of DifferenceKit
- Slightly more complex API, requiring more setup for basic use cases
- Larger codebase, which may increase app size marginally
Code Comparison
DeepDiff:
let changes = diff(old: oldItems, new: newItems)
tableView.reload(changes: changes, updateData: {
self.items = newItems
})
DifferenceKit:
let changeset = StagedChangeset(source: oldItems, target: newItems)
tableView.reload(using: changeset, with: .automatic) { data in
self.items = data
}
Both libraries offer similar functionality for diffing and updating UI elements, but DifferenceKit provides more granular control over the update process. DeepDiff has a simpler API for basic use cases, while DifferenceKit offers more advanced features for complex scenarios, especially when dealing with sectioned data.
A data-driven UICollectionView framework for building fast and flexible lists.
Pros of IGListKit
- More comprehensive solution for building complex, data-driven UICollectionViews
- Offers advanced features like diffing, batch updates, and automatic cell registration
- Backed by Instagram, ensuring ongoing maintenance and support
Cons of IGListKit
- Steeper learning curve due to its more complex architecture
- Heavier dependency, potentially increasing app size and compilation time
- May be overkill for simpler list implementations
Code Comparison
DeepDiff:
let changes = diff(old: oldItems, new: newItems)
tableView.reload(changes: changes, updateData: {
self.items = newItems
})
IGListKit:
let updater = ListAdapterUpdater()
let adapter = ListAdapter(updater: updater, viewController: self)
adapter.dataSource = self
adapter.collectionView = collectionView
Key Differences
- DeepDiff focuses solely on efficient diffing algorithms, while IGListKit provides a full framework for list management
- DeepDiff is more lightweight and easier to integrate for simple diffing needs
- IGListKit offers more advanced features and optimizations for complex list scenarios
Use Cases
- Choose DeepDiff for simple diffing requirements in existing UITableView or UICollectionView implementations
- Opt for IGListKit when building complex, data-driven lists with advanced requirements like heterogeneous cell types or complex animations
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
DeepDiff
â¤ï¸ Support my apps â¤ï¸
- Push Hero - pure Swift native macOS application to test push notifications
- PastePal - Pasteboard, note and shortcut manager
- Quick Check - smart todo manager
- Alias - App and file shortcut manager
- My other apps
â¤ï¸â¤ï¸ððð¤â¤ï¸â¤ï¸
DeepDiff tells the difference between 2 collections and the changes as edit steps. It also supports Texture, see Texture example
- Read more A better way to update UICollectionView data in Swift with diff framework
- Checkout Micro Fast diffing and type safe SwiftUI style data source for UICollectionView
Usage
Basic
The result of diff
is an array of changes, which is [Change]
. A Change
can be
.insert
: The item was inserted at an index.delete
: The item was deleted from an index.replace
: The item at this index was replaced by another item.move
: The same item has moved from this index to another index
By default, there is no .move
. But since .move
is just .delete
followed by .insert
of the same item, it can be reduced by specifying reduceMove
to true
.
Here are some examples
let old = Array("abc")
let new = Array("bcd")
let changes = diff(old: old, new: new)
// Delete "a" at index 0
// Insert "d" at index 2
let old = Array("abcd")
let new = Array("adbc")
let changes = diff(old: old, new: new)
// Move "d" from index 3 to index 1
let old = [
User(id: 1, name: "Captain America"),
User(id: 2, name: "Captain Marvel"),
User(id: 3, name: "Thor"),
]
let new = [
User(id: 1, name: "Captain America"),
User(id: 2, name: "The Binary"),
User(id: 3, name: "Thor"),
]
let changes = diff(old: old, new: new)
// Replace user "Captain Marvel" with user "The Binary" at index 1
DiffAware protocol
Model must conform to DiffAware
protocol for DeepDiff to work. An model needs to be uniquely identified via diffId
to tell if there have been any insertions or deletions. In case of same diffId
, compareContent
is used to check if any properties have changed, this is for replacement changes.
public protocol DiffAware {
associatedtype DiffId: Hashable
var diffId: DiffId { get }
static func compareContent(_ a: Self, _ b: Self) -> Bool
}
Some primitive types like String
, Int
, Character
already conform to DiffAware
Animate UITableView and UICollectionView
Changes to DataSource
can be animated by using batch update, as guided in Batch Insertion, Deletion, and Reloading of Rows and Sections
Since Change
returned by DeepDiff
follows the way batch update works, animating DataSource
changes is easy.
For safety, update your data source model inside updateData
to ensure synchrony inside performBatchUpdates
let oldItems = items
let newItems = DataSet.generateNewItems()
let changes = diff(old: oldItems, new: newItems)
collectionView.reload(changes: changes, section: 2, updateData: {
self.items = newItems
})
Take a look at Demo where changes are made via random number of items, and the items are shuffled.
How does it work
WagnerâFischer
If you recall from school, there is Levenshtein distance which counts the minimum edit distance to go from one string to another.
Based on that, the first version of DeepDiff
implements WagnerâFischer, which uses dynamic programming to compute the edit steps between 2 strings of characters. DeepDiff
generalizes this to make it work for any collection.
Some optimisations made
- Check empty old or new collection to return early
- Use
diffId
to quickly check that 2 items are not equal - Follow "We can adapt the algorithm to use less space, O(m) instead of O(mn), since it only requires that the previous row and current row be stored at any one time." to use 2 rows, instead of matrix to reduce memory storage.
The performance greatly depends on the number of items, the changes and the complexity of the equal
function.
WagnerâFischer algorithm
has O(mn) complexity, so it should be used for collection with < 100 items.
Heckel
The current version of DeepDiff
uses Heckel algorithm as described in A technique for isolating differences between files. It works on 2 observations about line occurrences and counters. The result is a bit lengthy compared to the first version, but it runs in linear time.
Thanks to
- Isolating Differences Between Files for explaining step by step
- HeckelDiff for a clever move reducer based on tracking
deleteOffset
More
There are other algorithms that are interesting
Benchmarks
Benchmarking is done on real device iPhone 6, with random items made of UUID strings (36 characters including hyphens), just to make comparisons more difficult.
You can take a look at the code Benchmark. Test is inspired from DiffUtil
Among different frameworks
Here are several popular diffing frameworks to compare
ðª From 2000 items to 2100 items (100 deletions, 200 insertions)
let (old, new) = generate(count: 2000, removeRange: 100..<200, addRange: 1000..<1200)
benchmark(name: "DeepDiff", closure: {
_ = DeepDiff.diff(old: old, new: new)
})
benchmark(name: "Dwifft", closure: {
_ = Dwifft.diff(old, new)
})
benchmark(name: "Changeset", closure: {
_ = Changeset.edits(from: old, to: new)
})
benchmark(name: "Differ", closure: {
_ = old.diffTraces(to: new)
})
benchmark(name: "ListDiff", closure: {
_ = ListDiff.List.diffing(oldArray: old, newArray: new)
})
Result
DeepDiff: 0.0450611114501953s
Differ: 0.199673891067505s
Dwifft: 149.603884935379s
Changeset: 77.5895738601685s
ListDiff: 0.105544805526733s
Increasing complexity
Here is how DeepDiff
handles large number of items and changes
ðª From 10000 items to 11000 items (1000 deletions, 2000 insertions)
DeepDiff: 0.233131170272827s
ðª From 20000 items to 22000 items (2000 deletions, 4000 insertions)
DeepDiff: 0.453393936157227s
ðª From 50000 items to 55000 items (5000 deletions, 10000 insertions)
DeepDiff: 1.04128122329712s
ðª From 100000 items to 1000000 items
Are you sure?
Installation
CocoaPods
Add the following to your Podfile
pod 'DeepDiff'
Carthage
Add the following to your Cartfile
github "onmyway133/DeepDiff"
Swift Package Manager
Add the following to your Package.swift file
.package(url: "https://github.com/onmyway133/DeepDiff.git", .upToNextMajor(from: "2.3.0"))
DeepDiff can also be installed manually. Just download and drop Sources
folders in your project.
Author
Khoa Pham, onmyway133@gmail.com
Contributing
We would love you to contribute to DeepDiff, check the CONTRIBUTING file for more info.
License
DeepDiff is available under the MIT license. See the LICENSE file for more info.
Top Related Projects
MIT Deep Learning Book in PDF format (complete and parts) by Ian Goodfellow, Yoshua Bengio and Aaron Courville
Commonly used data structures for Swift
Epoxy is a suite of declarative UI APIs for building UIKit applications in Swift
💻 A fast and flexible O(n) difference algorithm framework for Swift collection.
A data-driven UICollectionView framework for building fast and flexible lists.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot