Skip to content

Instantly share code, notes, and snippets.

@jpmcglone
Last active March 14, 2019 17:38
Show Gist options
  • Save jpmcglone/28a1a0083c15fc72a188 to your computer and use it in GitHub Desktop.
Save jpmcglone/28a1a0083c15fc72a188 to your computer and use it in GitHub Desktop.
array.filter into multiple arrays in one pass
public struct ConditionalArrayFilter <Element> {
let condition: (Element) -> Bool // the condition to meet
private var _array = Array<Element>()
var array: Array<Element> {
get { return _array }
}
init(condition: (Element) -> Bool) { self.condition = condition }
}
private extension ConditionalArrayFilter {
mutating func append(element: Element) {
if condition(element) {
_array.append(element)
}
}
}
extension Array {
func filter(filters:[ConditionalArrayFilter<Element>]) {
for element in self {
for var filter in filters {
filter.append(element)
}
}
}
}
/****************************/
/** Demo of the above code **/
/****************************/
class ConditionalArrayLoaderTest {
func test() {
// What's great is this is done in O(n)
let even = ConditionalArrayFilter<Int> { int in
return int % 2 == 0
}
let odd = ConditionalArrayFilter<Int> { int in
return int % 2 == 1
}
let by5 = ConditionalArrayFilter<Int> { int in
return int % 5 == 0
}
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
numbers.filter([even, odd, by5])
print(even.array)
print(odd.array)
print(by5.array)
}
}
@jpmcglone
Copy link
Author

Some math :D

Let n = size of array
Let k = number of conditions
Let i = number of cycles per iteration of array

Let c = number of cycles per condition check

Old way, using 3 .filters for 3 arrays, passing 3 times
k_(n_(c + i))
= k*(nc + in)
= knc + kin
= ckn + ikn

This way, using 1 .filter for 3 arrays, passing 1 time
n*(kc + i)
= nkc + ni
= ckn + in

Ratio
(ckn + ikn) / (ckn + in)
= (ck + ik) / (ck + i)
= k ((c + i) / (ck + i))

The old way is
k ((c + i) / (ck + i)) more cycles than the new way.


case 1:
n = 10,000
k = 5
i = 1 (assuming 1 cpu cycle. unlikely, but conservative)
c = 1 (assuming 1 cpu cycle. unlikely, but conservative)

old way:
1 * 5 * 10,000 + 1 * 5 * 10, 000
= 100,000

new way:
1 * 5 * 10,000 + 1 * 10,000
= 60,000

This is a significant improvement.
40% more efficient in this case.


case 2:

n = 1,000
k = 2
i = 2 (assuming i is 2 * c)
c = 1 (assuming 1 cpu cycle. unlikely, but conservative)

old way:
1 * 2 * 1,000 + 2 * 2 * 1,000 = 6,000

new way:
1 * 2 * 1,000 + 2 * 1,000 = 4,000

This is a significant improvement.
33% more efficient in this case.

@jpmcglone
Copy link
Author

It seems if the ratio c:i is large, the savings shrink and are maybe ignorable. But hey, this is just as easy to use as .filter 3 times, so any savings is better to me :)

@jpmcglone
Copy link
Author

Another bonus is, if you hold on to the ConditionalArrayFilter object, you can use the .condition closure to test another Element (of the same type) against it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment