Reputation: 975
I have two arrays of bool
s in Swift, not necessarily of the same size, and would like to do an element-wise OR between them.
Consider, e.g.,
var array1 = [false, true, false, true, false, true]
var array2 = [true, true, false, false]
What I would like to do is apply array2
as an OR-mask to a portion of array1
.
If I were to transcribe array2
into a portion of array1
, I would simply do this
array1[0..<array2.count] = array2[0..<array2.count]
But I would like to be able to write something like this:
array1[0..<array2.count] ||= array2[0..<array2.count]
which of course does not compile because ||=
is not available.
An explicit solution is to iterate through the elements in a classical way:
for i in 0..<array2.count {
array1[i] = array1[i] || array2[i]
}
but I wonder if there is a more elegant, Swift-like (maybe faster?) solution.
Upvotes: 1
Views: 253
Reputation: 73216
You can make use of the fact that the global zip(_:_:)
function creates a truncated zipped sequence with a the length that is the shorter on of the two supplied sequences.
If the two sequences passed to
zip(_:_:)
are different lengths, the resulting sequence is the same length as the shorter sequence.
To zip the indices of the array being transcribed into with the elements of the array being transcribed from. E.g. to construct a custom in-place replacement infix operator:
infix operator ||=: AssignmentPrecedence
func ||=(_ lhs: inout [Bool], rhs: [Bool]) {
zip(0..<lhs.count, rhs).forEach { if !lhs[$0] { lhs[$0] = $1 } }
//or: zip(0..<lhs.count, rhs).forEach { lhs[$0] = lhs[$0] || $1 }
}
Used as follows:
// array to be transcribed _into_ longer than the array
// being transcibed from
var array1 = [false, true, false, true, false, true]
var array2 = [true, true, false, false]
array1 ||= array2
print(array1) // [true, true, false, true, false, true]
// array to be transcribed _into_ shorter than the array
// being transcibed from
array2 = [true, true, false, false, true, true, false, false]
array1 ||= array2
print(array1) // [true, true, false, true, true, true]
Note also the following valuable comment from @zneak:
"The behavior for
||
on arrays of different size isn't obvious to me, so I think that it could be valuable to use a function with a representative name instead of an operator."
So you might want to considering implementing the logic above into a function with a semantically sound name rather than an operator.
Upvotes: 1
Reputation: 130201
I see no reason why you can't define the operator by yourself, e.g:
let array1 = [false, true, false, true, false, true]
let array2 = [true, true, false, false]
func ||(lhs: Array<Bool>, rhs: Array<Bool>) -> Array<Bool> {
let shorter: Array<Bool>
let longer: Array<Bool>
if lhs.count < rhs.count {
shorter = lhs
longer = rhs
} else {
longer = lhs
shorter = rhs
}
let shorterProlonged = shorter + longer.suffix(from: shorter.count)
return zip(shorterProlonged, longer).map { $0 || $1 }
}
print(array1 || array2)
Upvotes: 3
Reputation: 45210
If by "Swift-like" you mean a more declarative solution, you can:
zip
the arraysmap
the zipped tuples to produced an "or-ed" arrayHere's how it would look like:
let oredArray = zip(array1, array2).map { $0 || $1 }
array1[0 ..< min(oredArray.endIndex, array2.endIndex)] = ArraySlice(oredArray)
This does, however, require array1
to be mutable. Also, notice the use of min()
– it's a safeguard against array2
being longer than array1
.
Upvotes: 2