Skip to content
This repository has been archived by the owner on Mar 29, 2018. It is now read-only.

added methods and tests #72

Merged
merged 2 commits into from
Feb 3, 2015
Merged
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
55 changes: 55 additions & 0 deletions ExSwift/Array.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1098,6 +1098,61 @@ internal extension Array {
return sorted(isOrderedBefore)
}

/**
Runs a binary search to find the smallest element for which the block evaluates to true
The block should return true for all items in the array above a certain point and false for all items below a certain point
If that point is beyond the furthest item in the array, it returns nil

See http://ruby-doc.org/core-2.2.0/Array.html#method-i-bsearch regarding find-minimum mode for more

:param: block the block to run each time
:returns: the min element, or nil if there are no items for which the block returns true
*/
func bSearch (block: (T) -> (Bool)) -> T? {
if count == 0 {
return nil
}

var low = 0
var high = count - 1
while low <= high {
var mid = low + (high - low) / 2
if block(self[mid]) {
if mid == 0 || !block(self[mid - 1]) {
return self[mid]
} else {
high = mid
}
} else {
low = mid + 1
}
}

return nil
}

/**
Runs a binary search to find some element for which the block returns 0.
The block should return a negative number if the current value is before the target in the array, 0 if it's the target, and a positive number if it's after the target
The Spaceship operator is a perfect fit for this operation, e.g. if you want to find the object with a specific date and name property, you could keep the array sorted by date first, then name, and use this call:
let match = bSearch { [targetDate, targetName] <=> [$0.date, $0.name] }

See http://ruby-doc.org/core-2.2.0/Array.html#method-i-bsearch regarding find-any mode for more

:param: block the block to run each time
:returns: an item (there could be multiple matches) for which the block returns true
*/
func bSearch (block: (T) -> (Int)) -> T? {
let match = bSearch { item in
block(item) >= 0
}
if let match = match {
return block(match) == 0 ? match : nil
} else {
return nil
}
}

/**
Removes the last element from self and returns it.

Expand Down
31 changes: 30 additions & 1 deletion ExSwiftTests/ExSwiftArrayTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -511,5 +511,34 @@ class ExtensionsArrayTests: XCTestCase {
emptyArray.fill("foo")
XCTAssertEqual(emptyArray, [])
}
}

func testBSearchFindMin() {
1.upTo(10) { arraySize in
var testArray: [Int] = []
1.upTo(arraySize) { i in
testArray += [i]
}
for i in testArray {
XCTAssertEqual(testArray.bSearch({ $0 >= i })!, i)
}
}
XCTAssertTrue(array.bSearch({ $0 >= 101 }) == nil)
XCTAssertEqual(array.bSearch({ $0 >= 0 })!, 1)
XCTAssertTrue([].bSearch({ true }) == nil)
}

func testBSearchFindAny() {
1.upTo(10) { arraySize in
var testArray: [Int] = []
1.upTo(arraySize) { i in
testArray += [i]
}
for i in testArray {
XCTAssertEqual(testArray.bSearch({ $0 - i })!, i)
}
}
XCTAssertTrue(array.bSearch({ $0 - (self.array.max() + 1) }) == nil)
XCTAssertTrue(array.bSearch({ $0 - (self.array.min() - 1) }) == nil)
XCTAssertTrue([Int]().bSearch({ $0 }) == nil)
}
}