Since you know the both sequences to be increasing, you need only make use of the first and last element of the array a
. Using these, you can make use of the index(where:)
method to find the corresponding indices in the b
array, fulfilling the predicate >= (first of a)
and <= (last of a)
. E.g.
if let aFirst = a.first, let aLast = a.last,
let startIndex = b.index(where: { $0 >= aFirst }),
case let endIndex = b.index(where: { $0 > aLast }) ?? a.endIndex {
let c = b[startIndex..<endIndex] // note: this is an ArraySlice<Double>
// wrap in Array(..) to construct a new array
print(c)
// [0.0, 0.0040000000000000001, 0.0080000000000000002, 0.012, 0.016, 0.02, 0.024]
}
Or, as proposed by @MartinR, we can make a guess that we will, on avarage, more quickly fulfil the predicate of the second index(where:)
call if we start from the end of b
when checking for the first element that fulfills the $0 <= aLast
condition:
if let aFirst = a.first, let aLast = a.last,
let startIndex = b.index(where: { $0 >= aFirst }),
case let endIndex = b.reversed().index(where: { $0 <= aLast })!.base {
/* ^^^^^- as proposed by @MartinR */
let c = b[startIndex..<endIndex]
print(c)
// [0.0, 0.0040000000000000001, 0.0080000000000000002, 0.012, 0.016, 0.02, 0.024]
}
In the end this approach (as well as the filter
approach in the currently available other answers) runs in O(n)
. A more performant approach would be using binary search, which runs in O(log n)
asymptotically. As also mentioned by @MartinR, a good starting point could be the binary search Swift implementations from the Rosetta code:
as mentioned in this Q&A:
Answering the comment:
... I also have another issue that I forgot to mention. I have another
array y
that has exactly the same number of elements as b
. We can
regard these as X
and Y
values of a signal. I have to choose the
matching elements from y
for the elements I choose in b
.
The simplest solution would simply apply the calculated startIndex
and endIndex
to slice out a part of the y
array (just like you sliced out a part of b
to construct c
).
let d = y[startIndex..<endIndex]
// assumes y.count == b.count, and that these are not slices
// with non-matching indices
However, I believe a better approach would be using zip
. Simply zip y
and b
and use the result to construct an array of tuples, whereafter you can apply the predicate above to the b
members of the zipped sequence:
let y = Array(1...b.count)
if let aFirst = a.first, let aLast = a.last,
let startIndex = bAndY.index(where: { $0.0 >= aFirst }),
case let endIndex = bAndY.index(where: { $0.0 > aLast }) ?? a.endIndex {
let cAndD = bAndY[startIndex..<endIndex]
print(cAndD)
/* [(0.0040000000000000001, 2),
(0.0080000000000000002, 3),
(0.012, 4),
(0.016, 5),
(0.02, 6),
(0.024, 7)] */
}
Naturally constructing the cAndD
array will yield a minor overhead, but unless you're coding some really HPC application, you shouldn't need to worry about that.