Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .swift-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.0
6 changes: 6 additions & 0 deletions ArrayDiff.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,11 @@
TargetAttributes = {
CC71C1521BBDD4B400772EDB = {
CreatedOnToolsVersion = 7.0.1;
LastSwiftMigration = 0800;
};
CC71C15C1BBDD4B500772EDB = {
CreatedOnToolsVersion = 7.0.1;
LastSwiftMigration = 0800;
};
};
};
Expand Down Expand Up @@ -337,6 +339,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 3.0;
};
name = Debug;
};
Expand All @@ -355,6 +358,7 @@
PRODUCT_BUNDLE_IDENTIFIER = adlai.ArrayDiff;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 3.0;
};
name = Release;
};
Expand All @@ -365,6 +369,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = adlai.ArrayDiffTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
};
name = Debug;
};
Expand All @@ -375,6 +380,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = adlai.ArrayDiffTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
};
name = Release;
};
Expand Down
54 changes: 28 additions & 26 deletions ArrayDiff/ArrayDiff.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,28 @@ public struct ArrayDiff {
static var debugLogging = false

/// The indexes in the old array of the items that were kept
public let commonIndexes: NSIndexSet
public let commonIndexes: IndexSet
/// The indexes in the old array of the items that were removed
public let removedIndexes: NSIndexSet
public let removedIndexes: IndexSet
/// The indexes in the new array of the items that were inserted
public let insertedIndexes: NSIndexSet
public let insertedIndexes: IndexSet

/// Returns nil if the item was inserted
public func oldIndexForNewIndex(index: Int) -> Int? {
if insertedIndexes.containsIndex(index) { return nil }
public func oldIndexForNewIndex(_ index: Int) -> Int? {
if insertedIndexes.contains(index) { return nil }

var result = index
result -= insertedIndexes.countOfIndexesInRange(NSMakeRange(0, index))
result += removedIndexes.countOfIndexesInRange(NSMakeRange(0, result + 1))
result -= insertedIndexes.count(in: NSMakeRange(0, index).toRange()!)
result += removedIndexes.count(in: NSMakeRange(0, result + 1).toRange()!)
return result
}

/// Returns nil if the item was deleted
public func newIndexForOldIndex(index: Int) -> Int? {
if removedIndexes.containsIndex(index) { return nil }
public func newIndexForOldIndex(_ index: Int) -> Int? {
if removedIndexes.contains(index) { return nil }

var result = index
let deletedBefore = removedIndexes.countOfIndexesInRange(NSMakeRange(0, index))
let deletedBefore = removedIndexes.count(in: NSMakeRange(0, index).toRange()!)
result -= deletedBefore
var insertedAtOrBefore = 0
for i in insertedIndexes {
Expand Down Expand Up @@ -54,31 +54,31 @@ public struct ArrayDiff {

public extension Array {

public func diff(other: Array<Element>, elementsAreEqual: ((Element, Element) -> Bool)) -> ArrayDiff {
public func diff(_ other: Array<Element>, elementsAreEqual: ((Element, Element) -> Bool)) -> ArrayDiff {
var lengths: [[Int]] = Array<Array<Int>>(
count: count + 1,
repeatedValue: Array<Int>(
count: other.count + 1,
repeatedValue: 0)
repeating: Array<Int>(
repeating: 0,
count: other.count + 1),
count: count + 1
)

for i in (0...count).reverse() {
for j in (0...other.count).reverse() {
for i in (0...count).reversed() {
for j in (0...other.count).reversed() {
if i == count || j == other.count {
lengths[i][j] = 0
} else if elementsAreEqual(self[i], other[j]) {
lengths[i][j] = 1 + lengths[i+1][j+1]
} else {
lengths[i][j] = max(lengths[i+1][j], lengths[i][j+1])
lengths[i][j] = Swift.max(lengths[i+1][j], lengths[i][j+1])
}
}
}
let commonIndexes = NSMutableIndexSet()
var commonIndexes = IndexSet()
var i = 0, j = 0

while i < count && j < other.count {
if elementsAreEqual(self[i], other[j]) {
commonIndexes.addIndex(i)
commonIndexes.insert(i)
i += 1
j += 1
} else if lengths[i+1][j] >= lengths[i][j+1] {
Expand All @@ -87,12 +87,14 @@ public extension Array {
j += 1
}
}

let removedIndexes = NSMutableIndexSet(indexesInRange: NSMakeRange(0, count))
removedIndexes.removeIndexes(commonIndexes)

var removedIndexes = IndexSet(integersIn: 0..<count)
commonIndexes.forEach {
removedIndexes.remove($0)
}

let commonObjects = self[commonIndexes]
let addedIndexes = NSMutableIndexSet()
var addedIndexes = IndexSet()
i = 0
j = 0

Expand All @@ -101,7 +103,7 @@ public extension Array {
i += 1
j += 1
} else {
addedIndexes.addIndex(j)
addedIndexes.insert(j)
j += 1
}
}
Expand All @@ -111,7 +113,7 @@ public extension Array {
}

public extension Array where Element: Equatable {
public func diff(other: Array<Element>) -> ArrayDiff {
public func diff(_ other: Array<Element>) -> ArrayDiff {
return self.diff(other, elementsAreEqual: { $0 == $1 })
}
}
44 changes: 27 additions & 17 deletions ArrayDiff/FoundationExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,38 @@ import Foundation
// MARK: NSRange <-> Range<Int> conversion

public extension NSRange {
public var range: Range<Int> {
public var range: CountableRange<Int> {
return location..<location+length
}

public init(range: Range<Int>) {
location = range.startIndex
length = range.endIndex - range.startIndex
location = range.lowerBound
length = range.upperBound - range.lowerBound
}
}

// MARK: NSIndexSet -> [NSIndexPath] conversion

public extension NSIndexSet {
public extension IndexSet {
/**
Returns an array of NSIndexPaths that correspond to these indexes in the given section.

When reporting changes to table/collection view, you can improve performance by sorting
deletes in descending order and inserts in ascending order.
*/
public func indexPathsInSection(section: Int, ascending: Bool = true) -> [NSIndexPath] {
var result: [NSIndexPath] = []
public func indexPathsInSection(_ section: Int, ascending: Bool = true) -> [IndexPath] {
var result: [IndexPath] = []
result.reserveCapacity(count)
enumerateIndexesWithOptions(ascending ? [] : .Reverse) { index, _ in
result.append(NSIndexPath(indexes: [section, index], length: 2))
}

if ascending {
forEach { index in
result.append(IndexPath(indexes: [section, index]))
}
} else {
reversed().forEach { index in
result.append(IndexPath(indexes: [section, index]))
}
}
return result
}
}
Expand All @@ -37,26 +44,29 @@ public extension NSIndexSet {

public extension Array {

public subscript (indexes: NSIndexSet) -> [Element] {
public subscript (indexes: IndexSet) -> [Element] {
var result: [Element] = []
result.reserveCapacity(indexes.count)
indexes.enumerateRangesUsingBlock { nsRange, _ in

(indexes as NSIndexSet).enumerateRanges(options: []) { nsRange, _ in
result += self[nsRange.range]
}
return result
}

public mutating func removeAtIndexes(indexSet: NSIndexSet) {
indexSet.enumerateRangesWithOptions(.Reverse) { nsRange, _ in
self.removeRange(nsRange.range)
public mutating func removeAtIndexes(_ indexSet: IndexSet) {
(indexSet as NSIndexSet).enumerateRanges(options: .reverse) { nsRange, _ in
self.removeSubrange(nsRange.range)
}
}

public mutating func insertElements(newElements: [Element], atIndexes indexes: NSIndexSet) {
public mutating func insertElements(_ newElements: [Element], atIndexes indexes: IndexSet) {
assert(indexes.count == newElements.count)
var i = 0
indexes.enumerateRangesUsingBlock { range, _ in
self.insertContentsOf(newElements[i..<i+range.length], at: range.location)


(indexes as NSIndexSet).enumerateRanges(options: []) { range, _ in
self.insert(contentsOf: newElements[i..<i+range.length], at: range.location)
i += range.length
}
}
Expand Down
28 changes: 14 additions & 14 deletions ArrayDiff/Section.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,19 @@ public extension Array where Element: SectionType {
- Returns: A string like "<sectionCount: 4 itemCounts: [0: 16], [1: 25], [2: 17], [3: 4]>"
*/
public var nestedDescription: String {
let countsStr = enumerate().map { "[\($0): \($1.items.count)]" }.joinWithSeparator(", ")
let countsStr = enumerated().map { "[\($0): \($1.items.count)]" }.joined(separator: ", ")
return "<sectionCount: \(count) itemCounts: \(countsStr)>"
}

/**
Attempt to retrieve the item at the given index path. Returns nil if the index is out of bounds.
*/
public subscript (indexPath: NSIndexPath) -> Element.Item? {
let sectionIndex = indexPath.indexAtPosition(0)
public subscript (indexPath: IndexPath) -> Element.Item? {
let sectionIndex = indexPath[0]
guard indices.contains(sectionIndex) else { return nil }

let section = self[sectionIndex]
let itemIndex = indexPath.indexAtPosition(1)
let itemIndex = indexPath[1]
guard section.items.indices.contains(itemIndex) else { return nil }

return section.items[itemIndex]
Expand Down Expand Up @@ -60,11 +60,11 @@ public struct NestedDiff {
Determine the new index path, if any, for the given old index path.
- Returns: The index path after the update, or nil if the item was removed
*/
public func newIndexPathForOldIndexPath(indexPath: NSIndexPath) -> NSIndexPath? {
let oldSection = indexPath.indexAtPosition(0)
public func newIndexPathForOldIndexPath(_ indexPath: IndexPath) -> IndexPath? {
let oldSection = indexPath[0]
if let newSection = sectionsDiff.newIndexForOldIndex(oldSection),
newItem = itemDiffs[oldSection]?.newIndexForOldIndex(indexPath.indexAtPosition(1)) {
return NSIndexPath(indexes: [newSection, newItem], length: 2)
let newItem = itemDiffs[oldSection]?.newIndexForOldIndex(indexPath[1]) {
return (NSIndexPath(indexes: [newSection, newItem], length: 2) as IndexPath)
} else {
return nil
}
Expand All @@ -74,10 +74,10 @@ public struct NestedDiff {
Determine the new index path, if any, for the given old index path.
- Returns: The index path before the update, or nil if the item was inserted
*/
public func oldIndexPathForNewIndexPath(newIndexPath: NSIndexPath) -> NSIndexPath? {
if let oldSection = sectionsDiff.oldIndexForNewIndex(newIndexPath.indexAtPosition(0)),
oldItem = itemDiffs[oldSection]?.oldIndexForNewIndex(newIndexPath.indexAtPosition(1)) {
return NSIndexPath(indexes: [oldSection, oldItem], length: 2)
public func oldIndexPathForNewIndexPath(_ newIndexPath: IndexPath) -> IndexPath? {
if let oldSection = sectionsDiff.oldIndexForNewIndex(newIndexPath[0]),
let oldItem = itemDiffs[oldSection]?.oldIndexForNewIndex(newIndexPath[1]) {
return (NSIndexPath(indexes: [oldSection, oldItem], length: 2) as IndexPath)
} else {
return nil
}
Expand Down Expand Up @@ -109,11 +109,11 @@ public extension Array where Element: SectionType {
itemDiffs is indexed based on the _old_ section indexes.

*/
public func diffNested(newData: Array<Element>) -> NestedDiff {
public func diffNested(_ newData: Array<Element>) -> NestedDiff {
let sectionDiff = diff(newData)

// diffs will exist for all sections that weren't deleted or inserted
let itemDiffs: [ArrayDiff?] = self.enumerate().map { oldSectionIndex, oldSectionInfo in
let itemDiffs: [ArrayDiff?] = self.enumerated().map { oldSectionIndex, oldSectionInfo in
if let newSection = sectionDiff.newIndexForOldIndex(oldSectionIndex) {
assert(newData[newSection] == oldSectionInfo, "Diffing for the wrong section!")
return oldSectionInfo.items.diff(newData[newSection].items)
Expand Down
Loading