4

This is a construct I come across quite a lot. Is there a nice way to one-line it in Swift?

I could just write an extension on Sequence for it, but I feel like there's a "obvious" higher-order-function / set theory technique that is eluding me.

if array.contains(element) {
    array.removeObject(object: element)
}
else {
    array.append(element)
}

I don't think the solution will even necessarily be nicer per se, it's just something I think about every time I have to write this.

James Webster
  • 31,873
  • 11
  • 70
  • 114
  • Is there an option to use `Set` instead? – PGDev Sep 10 '19 at 11:22
  • 1
    the simple answer is not using an Array , try to use Set . Set doesn't accept duplicates so it won't be added if already exist. – SafoineMoncefAmine Sep 10 '19 at 11:24
  • @PGDev, let's say yes. I think I could colour in a Venn diagram of what I want, but I couldn't figure out how to code it using the options available in Swift's Set. – James Webster Sep 10 '19 at 11:27
  • @SafoineMoncefAmine, that's not quite what that code is doing. It's swapping the presence of the element in the array.. Removing it if it's present and adding it if not. – James Webster Sep 10 '19 at 11:28
  • 2
    The same in one line: ```array.contains(element) ? array.removeObject(object: element) : array.append(element)``` – Starsky Sep 10 '19 at 11:30
  • 1
    @Starsky, my colleague suggested that. She suffixed it with `//Technically one line ` – James Webster Sep 10 '19 at 11:31
  • @Starsky hahaha i was just answering that. Guess I should not anymore. – Galo Torres Sevilla Sep 10 '19 at 11:32
  • `if array.contains(element) { array.removeObject(object: element) } else { array.append(element) }` – Adrian Sep 10 '19 at 11:34
  • 1
    Rather than writing the shortest code try to write the most efficient code And there is no equivalent to NSArray's `removeObject` in Swift. – vadian Sep 10 '19 at 11:45
  • @vadian, rather than writing the "most efficient" code, profile your code occasionally and decide which bits are worth optimising. "Premature optimisation is the route of all evil" after all. – James Webster Sep 10 '19 at 11:48
  • @vadian: https://stackoverflow.com/a/28174952/916299 – James Webster Sep 10 '19 at 11:52
  • @JamesWebster I generally agree, but there's obvious stuff that you should avoid in almost any case. Your proposed "append if doesn't exist" API would be `O(n)`, and undoubtedly called as part of some loop that's at least `O(n)`. `O(n^2)` explodes pretty fast, at very common real life data sets. People have thousands of songs, contacts, 10s/100s of thousands of files, etc. – Alexander Sep 10 '19 at 13:02
  • @JamesWebster I know this looks funny and easy, but you actually asked for a one-liner )))) – Starsky Sep 10 '19 at 14:13

3 Answers3

9

I've found the part of Set Theory that was eluding me! The result I want is the Symmetric Difference of the two arrays and this is included in Swift's set:

var element = Set([1])
var set = Set([1, 2, 3])

set = set.symmetricDifference(element) //2, 3
set = set.symmetricDifference(element) //1, 2, 3
James Webster
  • 31,873
  • 11
  • 70
  • 114
  • You don't need to convert the element to a set, you can just write `set = set.symmetricDifference([1])` – Daniel Mar 11 '21 at 16:24
  • [formSymmetricDifference](https://developer.apple.com/documentation/swift/set/formsymmetricdifference(_:)-22p0m) –  Aug 27 '22 at 17:58
1

You can try using Set instead,

var set: Set<Int> = [1, 2, 4]
if !set.insert(4).inserted {
    set.remove(4)
}
PGDev
  • 23,751
  • 6
  • 34
  • 88
-1

Based on the great answer by. James Webster. We can enhance for reusability.

extension Set {
    /// Inserts given element if it doesn't exist in the set or removes it.
    ///
    mutating func withSymmetricDifference(_ element: Element) {
        self = symmetricDifference([element])
    }
}

Usage

var set = Set([1, 2, 3])
set.withSymmetricDifference(1) // [1, 2, 3]
set.withSymmetricDifference(1) // [2, 3]
Ahmed M. Hassan
  • 709
  • 9
  • 14