How can I unset/remove an element from an array in Apple's new language Swift?
Here's some code:
let animals = ["cats", "dogs", "chimps", "moose"]
How could the element animals[2] be removed from the array?
The let keyword is for declaring constants that can't be changed. If you want to modify a variable you should use var instead, e.g:
var animals = ["cats", "dogs", "chimps", "moose"]
animals.remove(at: 2) //["cats", "dogs", "moose"]
A non-mutating alternative that will keep the original collection unchanged is to use filter to create a new collection without the elements you want removed, e.g:
let pets = animals.filter { $0 != "chimps" }
Given
var animals = ["cats", "dogs", "chimps", "moose"]
Remove first element
animals.removeFirst() // "cats"
print(animals) // ["dogs", "chimps", "moose"]
Remove last element
animals.removeLast() // "moose"
print(animals) // ["cats", "dogs", "chimps"]
Remove element at index
animals.remove(at: 2) // "chimps"
print(animals) // ["cats", "dogs", "moose"]
Remove element of unknown index
For only one element
if let index = animals.firstIndex(of: "chimps") {
animals.remove(at: index)
}
print(animals) // ["cats", "dogs", "moose"]
For multiple elements
var animals = ["cats", "dogs", "chimps", "moose", "chimps"]
animals = animals.filter(){$0 != "chimps"}
print(animals) // ["cats", "dogs", "moose"]
Notes
The above methods modify the array in place (except for filter) and return the element that was removed.
Swift Guide to Map Filter Reduce
If you don't want to modify the original array, you can use dropFirst or dropLast to create a new array.
Updated to Swift 5.2
The above answers seem to presume that you know the index of the element that you want to delete.
Often you know the reference to the object you want to delete in the array. (You iterated through your array and have found it, e.g.) In such cases it might be easier to work directly with the object reference without also having to pass its index everywhere. Hence, I suggest this solution. It uses the identity operator !==, which you use to test whether two object references both refer to the same object instance.
func delete(element: String) {
list = list.filter { $0 != element }
}
Of course this doesn't just work for Strings.
Swift 5:
Here is a cool and easy extension to remove elements in an array, without filtering :
extension Array where Element: Equatable {
// Remove first collection element that is equal to the given `object`:
mutating func remove(object: Element) {
guard let index = firstIndex(of: object) else {return}
remove(at: index)
}
}
Usage :
var myArray = ["cat", "barbecue", "pancake", "frog"]
let objectToRemove = "cat"
myArray.remove(object: objectToRemove) // ["barbecue", "pancake", "frog"]
Also works with other types, such as Int since Element is a generic type:
var myArray = [4, 8, 17, 6, 2]
let objectToRemove = 17
myArray.remove(object: objectToRemove) // [4, 8, 6, 2]
For Swift4:
list = list.filter{$0 != "your Value"}
As of Xcode 10+, and according to the WWDC 2018 session 223, "Embracing Algorithms," a good method going forward will be mutating func removeAll(where predicate: (Element) throws -> Bool) rethrows
Apple's example:
var phrase = "The rain in Spain stays mainly in the plain."
let vowels: Set<Character> = ["a", "e", "i", "o", "u"]
phrase.removeAll(where: { vowels.contains($0) })
// phrase == "Th rn n Spn stys mnly n th pln."
see Apple's Documentation
So in the OP's example, removing animals[2], "chimps":
var animals = ["cats", "dogs", "chimps", "moose"]
animals.removeAll(where: { $0 == "chimps" } )
// or animals.removeAll { $0 == "chimps" }
This method may be preferred because it scales well (linear vs quadratic), is readable and clean. Keep in mind that it only works in Xcode 10+, and as of writing this is in Beta.
If you have array of custom Objects, you can search by specific property like this:
if let index = doctorsInArea.firstIndex(where: {$0.id == doctor.id}){
doctorsInArea.remove(at: index)
}
or if you want to search by name for example
if let index = doctorsInArea.firstIndex(where: {$0.name == doctor.name}){
doctorsInArea.remove(at: index)
}
You could do that. First make sure Dog really exists in the array, then remove it. Add the for statement if you believe Dog may happens more than once on your array.
var animals = ["Dog", "Cat", "Mouse", "Dog"]
let animalToRemove = "Dog"
for object in animals {
if object == animalToRemove {
animals.remove(at: animals.firstIndex(of: animalToRemove)!)
}
}
If you are sure Dog exits in the array and happened only once just do that:
animals.remove(at: animals.firstIndex(of: animalToRemove)!)
If you have both, strings and numbers
var array = [12, 23, "Dog", 78, 23]
let numberToRemove = 23
let animalToRemove = "Dog"
for object in array {
if object is Int {
// this will deal with integer. You can change to Float, Bool, etc...
if object == numberToRemove {
array.remove(at: array.firstIndex(of: numberToRemove)!)
}
}
if object is String {
// this will deal with strings
if object == animalToRemove {
array.remove(at: array.firstIndex(of: animalToRemove)!)
}
}
}
If you don't know the index of the element that you want to remove, and the element is conform the Equatable protocol, you can do:
animals.remove(at: animals.firstIndex(of: "dogs")!)
See Equatable protocol answer:How do I do indexOfObject or a proper containsObject
Few Operation relates to Array in Swift
Create Array
var stringArray = ["One", "Two", "Three", "Four"]
Add Object in Array
stringArray = stringArray + ["Five"]
Get Value from Index object
let x = stringArray[1]
Append Object
stringArray.append("At last position")
Insert Object at Index
stringArray.insert("Going", at: 1)
Remove Object
stringArray.remove(at: 3)
Concat Object value
var string = "Concate Two object of Array \(stringArray[1]) + \(stringArray[2])"
Remove elements using indexes array:
Array of Strings and indexes
let animals = ["cats", "dogs", "chimps", "moose", "squarrel", "cow"]
let indexAnimals = [0, 3, 4]
let arrayRemainingAnimals = animals
.enumerated()
.filter { !indexAnimals.contains($0.offset) }
.map { $0.element }
print(arrayRemainingAnimals)
//result - ["dogs", "chimps", "cow"]
Array of Integers and indexes
var numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
let indexesToRemove = [3, 5, 8, 12]
numbers = numbers
.enumerated()
.filter { !indexesToRemove.contains($0.offset) }
.map { $0.element }
print(numbers)
//result - [0, 1, 2, 4, 6, 7, 9, 10, 11]
Remove elements using element value of another array
Arrays of integers
let arrayResult = numbers.filter { element in
return !indexesToRemove.contains(element)
}
print(arrayResult)
//result - [0, 1, 2, 4, 6, 7, 9, 10, 11]
Arrays of strings
let arrayLetters = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
let arrayRemoveLetters = ["a", "e", "g", "h"]
let arrayRemainingLetters = arrayLetters.filter {
!arrayRemoveLetters.contains($0)
}
print(arrayRemainingLetters)
//result - ["b", "c", "d", "f", "i"]
I came up with the following extension that takes care of removing elements from an Array, assuming the elements in the Array implement Equatable:
extension Array where Element: Equatable {
mutating func removeEqualItems(_ item: Element) {
self = self.filter { (currentItem: Element) -> Bool in
return currentItem != item
}
}
mutating func removeFirstEqualItem(_ item: Element) {
guard var currentItem = self.first else { return }
var index = 0
while currentItem != item {
index += 1
currentItem = self[index]
}
self.remove(at: index)
}
}
Usage:
var test1 = [1, 2, 1, 2]
test1.removeEqualItems(2) // [1, 1]
var test2 = [1, 2, 1, 2]
test2.removeFirstEqualItem(2) // [1, 1, 2]
Regarding #Suragch's Alternative to "Remove element of unknown index":
There is a more powerful version of "indexOf(element)" that will match on a predicate instead of the object itself. It goes by the same name but it called by myObjects.indexOf{$0.property = valueToMatch}. It returns the index of the first matching item found in myObjects array.
If the element is an object/struct, you may want to remove that element based on a value of one of its properties. Eg, you have a Car class having car.color property, and you want to remove the "red" car from your carsArray.
if let validIndex = (carsArray.indexOf{$0.color == UIColor.redColor()}) {
carsArray.removeAtIndex(validIndex)
}
Foreseeably, you could rework this to remove "all" red cars by embedding the above if statement within a repeat/while loop, and attaching an else block to set a flag to "break" out of the loop.
Swift 5
guard let index = orders.firstIndex(of: videoID) else { return }
orders.remove(at: index)
This should do it (not tested):
animals[2...3] = []
Edit: and you need to make it a var, not a let, otherwise it's an immutable constant.
extension to remove String object
extension Array {
mutating func delete(element: String) {
self = self.filter() { $0 as! String != element }
}
}
I use this extension, almost same as Varun's, but this one (below) is all-purpose:
extension Array where Element: Equatable {
mutating func delete(element: Iterator.Element) {
self = self.filter{$0 != element }
}
}
To remove elements from an array, use the remove(at:),
removeLast() and removeAll().
yourArray = [1,2,3,4]
Remove the value at 2 position
yourArray.remove(at: 2)
Remove the last value from array
yourArray.removeLast()
Removes all members from the set
yourArray.removeAll()
Related
I want to subtract array1 by array2
Example:
var array1 = ["the", "people", "prefer", "to", "go", "to", "the","sun","beach"]
var array2 = ["the", "people", "prefer", "go", "to", "the", "moon","beach"]
I want Output:
["to","sun"]
What I am trying so far:
let reuslt = array1.filter { ! array2.contains($0) }
Output:
["sun"]
it's checking to contain a matching item removing all items if it matches but I want to remove one for one.
Just do it on the computer the way you would do it in your brain. Loop through array2 (not array1). For each element of array2, if that element has a firstIndex in array1, remove the element at that index from array1.
for word in array2 {
if let index = array1.firstIndex(of: word) {
array1.remove(at: index)
}
}
What you effectively want to do is this for loop
for item2 in array2 {
for i in 0..<array1.count {
if item2 == array1[i] {
array1.remove(at: i)
break
}
}
}
Filter works in exactly this way except it doesn't break on the first item but continues to remove all items.
You can also put this into a one liner like this with map instead of filter: array2.map({guard let i = array1.firstIndex(of: $0) else {return}; array1.remove(at: i)})
Like above answers, it is ok for a loop contains findIndex and remove from array.
But in other world, I think if the array is too large, the complexity of firstIndex(of:) and remove(at:) cause time and CPU too much for this task - Heat of device can raise a lots too. You can minimize it by using dictionary.
This is an another approach:
func findTheDiff(_ compareArr: [String], _ desArr: [String]) -> [String] {
var resultArr : [String] = []
var dict : [String: Int] = [:]
for word in compareArr {
if dict[word] == nil {
dict[word] = 1
} else {
dict[word] = dict[word]! + 1
}
}
for checkWord in desArr {
if dict[checkWord] != nil && dict[checkWord]! > 0 {
dict[checkWord] = dict[checkWord]! - 1
continue
}
resultArr.append(checkWord)
}
return resultArr
}
Usage:
var array1 = ["the", "people", "prefer", "to", "go", "to", "the","sun","beach"]
var array2 = ["the", "people", "prefer", "go", "to", "the", "moon","beach"]
var result = self.findTheDiff(array2, array1)
print(result) // ["to", "sun"]
You can find the complexity of firstIndex, remove(at:) below:
https://developer.apple.com/documentation/swift/array/firstindex(of:)
https://developer.apple.com/documentation/swift/array/remove(at:)-1p2pj
#Thang Phi's answer has the right idea. This is not different, but it works with a level of abstraction that incorporates the "counted set" idea for which Swift doesn't yet provide a built-in type:
import OrderedCollections
public extension Sequence where Element: Hashable {
/// A version of this sequence without the earliest occurrences of all `elementsToRemove`.
///
/// If `elementsToRemove` contains multiple equivalent values,
/// that many of the earliest occurrences will be filtered out.
func filteringOutEarliestOccurrences(from elementsToRemove: some Sequence<Element>) -> some Sequence<Element> {
var elementCounts = Dictionary(bucketing: elementsToRemove)
return lazy.filter {
do {
try elementCounts.remove(countedSetElement: $0)
return false
} catch {
return true
}
}
}
}
public extension Dictionary where Value == Int {
/// Create "buckets" from a sequence of keys,
/// such as might be used for a histogram.
/// - Note: This can be used for a "counted set".
#inlinable init(bucketing unbucketedKeys: some Sequence<Key>) {
self.init(zip(unbucketedKeys, 1), uniquingKeysWith: +)
}
/// Treating this dictionary as a "counted set", reduce the element's value by 1.
/// - Throws: If `countedSetElement` is not a key.
#inlinable mutating func remove(countedSetElement: Key) throws {
guard let count = self[countedSetElement] else { throw AnyError() }
self[countedSetElement] = count == 1 ? nil : count - 1
}
}
/// `zip` a sequence with a single value, instead of another sequence.
#inlinable public func zip<Sequence: Swift.Sequence, Constant>(
_ sequence: Sequence, _ constant: Constant
) -> some Swift.Sequence<(Sequence.Element, Constant)> {
zip(sequence, **ModuleName**.sequence(constant))
}
/// An infinite sequence of a single value.
#inlinable public func sequence<Element>(_ element: Element) -> some Sequence<Element> {
let getSelf: (Element) -> Element = \.self
return sequence(first: element, next: getSelf)
}
/// A nondescript error.
public struct AnyError: Error & Equatable {
public init() { }
}
Your example seems a little confusing / incomplete on the output. But sounds like you could do something like this:
extension Array where Element: Hashable {
func difference(from other: [Element]) -> [Element] {
let thisSet = Set(self)
let otherSet = Set(other)
return Array(thisSet.symmetricDifference(otherSet))
}
}
let names1 = ["the", "people", "prefer", "to", "go", "to", "the","sun","beach"]
let names2 = ["the", "people", "prefer", "go", "to", "the", "moon","beach"]
let difference = names1.difference(from: names2)
print(Array(difference)) // ["sun", "moon"]
Using an extension will of course make this code available to all of your arrays in your project. Since we convert the arrays into Sets, duplicates are removed, which may be a issue in your use case.
This Array extension was taken form: https://www.hackingwithswift.com/example-code/language/how-to-find-the-difference-between-two-arrays A vital resoruce for all things swift, especially SwiftUI.
Simple subtraction method that works for Equatable types including String.
extension Array where Element: Equatable {
func subtracting(_ array: Array<Element>) -> Array<Element> {
var result: Array<Element> = []
var toSub = array
for i in self {
if let index = toSub.firstIndex(of: i) {
toSub.remove(at: index)
continue
}
else {
result.append(i)
}
}
return result
}
}
let first = [1, 1, 2, 3, 3, 5, 6, 7, 7]
let second = [2, 2, 3, 4, 4, 5, 5, 6]
let result = first.subtracting(second)
//print result
//[1, 1, 3, 7, 7]
I am trying to write an extension for Array Types that sums the n-previous indexes in the index n.
let myArray = [1, 2, 3, 4, 5]
let mySumArray = myArray.sumNIndex()
print(mySumArray)
// returns [1,3,6,10,15]
I have tried various approaches which all failed at some point.
For instance, the example hereafter triggers a compile error
"Cannot invoke 'reduce' with an argument list of type '(Int, _)'":
extension Array {
mutating func indexSum() {
var tempArray = [Any]()
for index in 1...self.count - 1 {
self[index] += self[.prefix(index + 2).reduce(0, +)]
}
}
}
This other attempt triggers another compile error:
"Binary operator '+=' cannot be applied to two 'Element' operands"
extension Array {
mutating func indexSum() {
var tempArray = [Any]()
for index in 1...self.count - 1 {
self[index] += self[index - 1]
}
}
}
Any idea is welcome!
Thank you very much for your help!
EDIT: Many thanks to #Martin and #Carpsen who figured it out in 2 different ways
#Martin using map method:
extension Array where Element: Numeric {
func cumulativeSum() -> [Element] {
var currentSum: Element = 0
return map {
currentSum += $0
return currentSum
}
}
}
#Carpsen using reduce method:
extension Array where Element: Numeric {
func indexSum() -> [Element] {
return self.reduce(into: [Element]()) {(acc, element) in
return acc + [(acc.last ?? 0) + element]
})
}
}
The main problem is that the addition operator + is not defined for elements
of arbitrary arrays. You need to restrict the extension method, e.g. to
arrays of Numeric elements.
Also there is no need to use Any.
Here is a possible implementation as a non-mutating method:
extension Array where Element: Numeric {
func cumulativeSum() -> [Element] {
var currentSum: Element = 0
return map {
currentSum += $0
return currentSum
}
}
}
Examples:
let intArray = [1, 2, 3, 4, 5]
print(intArray.cumulativeSum()) // [1, 3, 6, 10, 15]
let floatArray = [1.0, 2.5, 3.25]
print(floatArray.cumulativeSum()) [1.0, 3.5, 6.75]
In a similar fashion we can “cumulatively join” the elements of a
string array. enumerated() is now used to provide the current element
index together with the element, and that is used to decide whether to
insert the separator or not:
extension Array where Element == String {
func cumulativeJoin(separator: String) -> [Element] {
var currentJoin = ""
return enumerated().map { (offset, elem) in
if offset > 0 { currentJoin.append(separator) }
currentJoin.append(elem)
return currentJoin
}
}
}
Examples:
let stringArray = ["a", "b", "c"]
print(stringArray.cumulativeJoin()) // ["a", "ab", "abc"]
print(stringArray.cumulativeJoin(separator: ":")) // ["a", "a:b", "a:b:c"]
Try this:
let myArray = [1, 2, 3, 4, 5]
myArray.reduce([Int](), {accumulator, element in
return accumulator + [(accumulator.last ?? 0) + element]
})
//[1, 3, 6, 10, 15]
What this reduce does is:
Start with an empty array
With each element from myArray it calculates its sum with the last element in the accumulator
Return the previous array plus the last sum
Here is a simpler, but longer version:
let myArray = [1, 2, 3, 4, 5]
let newArray = myArray.reduce([Int](), {accumulator, element in
var tempo = accumulator
let lastElementFromTheAccumulator = accumulator.last ?? 0
let currentSum = element + lastElementFromTheAccumulator
tempo.append(currentSum)
return tempo
})
print(newArray) //[1, 3, 6, 10, 15]
A more efficient solution, as suggested by Martin R in the comments, uses reduce(into:):
myArray.reduce(into: [Int]()) { (accumulator, element) in
accumulator += [(accumulator.last ?? 0) + element]
}
//[1, 3, 6, 10, 15]
And you could have it as an extension:
extension Array where Element: Numeric {
func indexSum() -> [Element] {
return self.reduce([Element](), {acc, element in
return acc + [(acc.last ?? 0) + element]
})
}
}
myArray.indexSum() //[1, 3, 6, 10, 15]
Here a solution that will work with strings too:
extension Array where Element == String {
func indexSum() -> [String] {
return self.reduce(into: [String]()) {(accumulator, element) in
accumulator += [(accumulator.last ?? "") + element]
}
}
}
["a", "b", "c", "d"].indexSum() //["a", "ab", "abc", "abcd"]
If you'd like to have a separator between the elements of the initial array elements, you could use this extension:
extension Array where Element == String {
func indexSum(withSparator: String) -> [String] {
return self.reduce(into: [String]()) {(accumulator, element) in
var previousString = ""
if let last = accumulator.last {
previousString = last + " "
}
accumulator += [previousString + element]
}
}
}
["a", "b", "c", "d"].indexSum(withSparator: " ") //["a", "a b", "a b c", "a b c d"]
How do I split a deck of cards? I have an array made and a random card dealer, but have no idea how to split the deck.
Thanks everyone for the help! I now have a working card app, did run into other problems but they were solved quickly.
You can make an extension so it can return an array of two arrays, working with Ints, Strings, etc:
extension Array {
func split() -> [[Element]] {
let ct = self.count
let half = ct / 2
let leftSplit = self[0 ..< half]
let rightSplit = self[half ..< ct]
return [Array(leftSplit), Array(rightSplit)]
}
}
let deck = ["J", "Q", "K", "A"]
let nums = [0, 1, 2, 3, 4]
deck.split() // [["J", "Q"], ["K", "A"]]
nums.split() // [[0, 1], [2, 3, 4]]
But returning a named tuple is even better, because it enforces the fact that you expect exactly two arrays as a result:
extension Array {
func split() -> (left: [Element], right: [Element]) {
let ct = self.count
let half = ct / 2
let leftSplit = self[0 ..< half]
let rightSplit = self[half ..< ct]
return (left: Array(leftSplit), right: Array(rightSplit))
}
}
let deck = ["J", "Q", "K", "A"]
let splitDeck = deck.split()
print(splitDeck.left) // ["J", "Q"]
print(splitDeck.right) // ["K", "A"]
Note: credits goes to Andrei and Qbyte for giving the first correct answer, I'm just adding info.
You can use subscript range
let deck: [String] = ["J", "Q", "K", "A"]
// use ArraySlice only for transient computation
let leftSplit: ArraySlice<String> = deck[0 ..< deck.count / 2] // "J", "Q"
let rightSplit: ArraySlice<String> = deck[deck.count / 2 ..< deck.count] // "K", "A"
// make arrays from ArraySlice
let leftDeck: [String] = Array(leftSplit) // "J", "Q"
let rightDeck: [String] = Array(rightSplit) // "K", "A"
EDIT: above code is for Swift 2, maybe for Swift 3 is a more convenient way.
Swift
More generic solution to split the array into chunks the answer from this link
extension Array {
func chunked(into size: Int) -> [[Element]] {
return stride(from: 0, to: count, by: size).map {
Array(self[$0 ..< Swift.min($0 + size, count)])
}
}
}
let numbers = Array(1...100)
let result = numbers.chunked(into: 5)
And one more realization of previously provided ideas. Firstly, up to Swift current documentation, it is better to choose names in past simple tense for functions that produce some result and present tense for mutating ones.
As second, as for me, it is better to choose half adding count % 2 to give more uniformed result.
Here is it:
extension Array {
func devided() -> ([Element], [Element]) {
let half = count / 2 + count % 2
let head = self[0..<half]
let tail = self[half..<count]
return (Array(head), Array(tail))
}
}
And results:
let set1 = [1, 2, 3, 4, 5, 6, 7,8]
let set2 = [1, 2, 3, 4, 5]
let set3 = [1]
let set4 = [Int]()
print(set1.devided())
print(set2.devided())
print(set3.devided())
print(set4.devided())
([1, 2, 3, 4], [5, 6, 7, 8])
([1, 2, 3], [4, 5])
([1], [])
([], [])
You can create an extension on SequenceType, and create a function named divide.
This function would iterate through the elements of the sequence while placing those that match the predicate into one array (slice) and those that do not match into another array (remainder).
The function returns a tuple containing the slice and the remainder.
extension SequenceType {
/**
Returns a tuple with 2 arrays.
The first array (the slice) contains the elements of self that match the predicate.
The second array (the remainder) contains the elements of self that do not match the predicate.
*/
func divide(#noescape predicate: (Self.Generator.Element) -> Bool) -> (slice: [Self.Generator.Element], remainder: [Self.Generator.Element]) {
var slice: [Self.Generator.Element] = []
var remainder: [Self.Generator.Element] = []
forEach {
switch predicate($0) {
case true : slice.append($0)
case false : remainder.append($0)
}
}
return (slice, remainder)
}
}
This is an example
let tuple = [1, 2, 3, 4, 5].divide({ $0 >= 3 })
tuple.slice // [3, 4, 5]
tuple.remainder // [1, 2]
I have two arrays like this:
var arrayA = ["Mike", "James", "Stacey", "Steve"]
var arrayB = ["Steve", "Gemma", "James", "Lucy"]
As you can see, James and Steve match and I want to be able to remove them from arrayA. How would I write this?
#francesco-vadicamo's answer in Swift 2/3/4+
arrayA = arrayA.filter { !arrayB.contains($0) }
The easiest way is by using the new Set container (added in Swift 1.2 / Xcode 6.3):
var setA = Set(arrayA)
var setB = Set(arrayB)
// Return a set with all values contained in both A and B
let intersection = setA.intersect(setB)
// Return a set with all values in A which are not contained in B
let diff = setA.subtract(setB)
If you want to reassign the resulting set to arrayA, simply create a new instance using the copy constructor and assign it to arrayA:
arrayA = Array(intersection)
The downside is that you have to create 2 new data sets.
Note that intersect doesn't mutate the instance it is invoked in, it just returns a new set.
There are similar methods to add, subtract, etc., you can take a look at them
Like this:
var arrayA = ["Mike", "James", "Stacey", "Steve"]
var arrayB = ["Steve", "Gemma", "James", "Lucy"]
for word in arrayB {
if let ix = find(arrayA, word) {
arrayA.removeAtIndex(ix)
}
}
// now arrayA is ["Mike", "Stacey"]
I agree with Antonio's answer, however for small array subtractions you can also use a filter closure like this:
let res = arrayA.filter { !contains(arrayB, $0) }
matt and freytag's solutions are the ONLY ones that account for duplicates and should be receiving more +1s than the other answers.
Here is an updated version of matt's answer for Swift 3.0:
var arrayA = ["Mike", "James", "Stacey", "Steve"]
var arrayB = ["Steve", "Gemma", "James", "Lucy"]
for word in arrayB {
if let ix = arrayA.index(of: word) {
arrayA.remove(at: ix)
}
}
Original answer
This can also be implemented as a minus func:
func -<T:RangeReplaceableCollectionType where T.Generator.Element:Equatable>( lhs:T, rhs:T ) -> T {
var lhs = lhs
for element in rhs {
if let index = lhs.indexOf(element) { lhs.removeAtIndex(index) }
}
return lhs
}
Now you can use
arrayA - arrayB
Updated implementation for Swift 5
func -<T: RangeReplaceableCollection>(lhs: T, rhs: T) -> T where T.Iterator.Element: Equatable {
var lhs = lhs
for element in rhs {
if let index = lhs.firstIndex(of: element) { lhs.remove(at: index) }
}
return lhs
}
Using the Array → Set → Array method mentioned by Antonio, and with the convenience of an operator, as freytag pointed out, I've been very satisfied using this:
// Swift 3.x/4.x
func - <Element: Hashable>(lhs: [Element], rhs: [Element]) -> [Element]
{
return Array(Set<Element>(lhs).subtracting(Set<Element>(rhs)))
}
For smaller arrays I use:
/* poormans sub for Arrays */
extension Array where Element: Equatable {
static func -=(lhs: inout Array, rhs: Array) {
rhs.forEach {
if let indexOfhit = lhs.firstIndex(of: $0) {
lhs.remove(at: indexOfhit)
}
}
}
static func -(lhs: Array, rhs: Array) -> Array {
return lhs.filter { return !rhs.contains($0) }
}
}
Remove elements using indexes array:
Array of Strings and indexes
let animals = ["cats", "dogs", "chimps", "moose", "squarrel", "cow"]
let indexAnimals = [0, 3, 4]
let arrayRemainingAnimals = animals
.enumerated()
.filter { !indexAnimals.contains($0.offset) }
.map { $0.element }
print(arrayRemainingAnimals)
//result - ["dogs", "chimps", "cow"]
Array of Integers and indexes
var numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
let indexesToRemove = [3, 5, 8, 12]
numbers = numbers
.enumerated()
.filter { !indexesToRemove.contains($0.offset) }
.map { $0.element }
print(numbers)
//result - [0, 1, 2, 4, 6, 7, 9, 10, 11]
Remove elements using element value of another array
Arrays of integers
let arrayResult = numbers.filter { element in
return !indexesToRemove.contains(element)
}
print(arrayResult)
//result - [0, 1, 2, 4, 6, 7, 9, 10, 11]
Arrays of strings
let arrayLetters = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
let arrayRemoveLetters = ["a", "e", "g", "h"]
let arrayRemainingLetters = arrayLetters.filter {
!arrayRemoveLetters.contains($0)
}
print(arrayRemainingLetters)
//result - ["b", "c", "d", "f", "i"]
In Swift, how can I check if an element exists in an array? Xcode does not have any suggestions for contain, include, or has, and a quick search through the book turned up nothing. Any idea how to check for this? I know that there is a method find that returns the index number, but is there a method that returns a boolean like ruby's #include??
Example of what I need:
var elements = [1,2,3,4,5]
if elements.contains(5) {
//do something
}
Swift 2, 3, 4, 5:
let elements = [1, 2, 3, 4, 5]
if elements.contains(5) {
print("yes")
}
contains() is a protocol extension method of SequenceType (for sequences of Equatable elements) and not a global method as in
earlier releases.
Remarks:
This contains() method requires that the sequence elements
adopt the Equatable protocol, compare e.g. Andrews's answer.
If the sequence elements are instances of a NSObject subclass
then you have to override isEqual:, see NSObject subclass in Swift: hash vs hashValue, isEqual vs ==.
There is another – more general – contains() method which does not require the elements to be equatable and takes a predicate as an
argument, see e.g. Shorthand to test if an object exists in an array for Swift?.
Swift older versions:
let elements = [1,2,3,4,5]
if contains(elements, 5) {
println("yes")
}
For those who came here looking for a find and remove an object from an array:
Swift 1
if let index = find(itemList, item) {
itemList.removeAtIndex(index)
}
Swift 2
if let index = itemList.indexOf(item) {
itemList.removeAtIndex(index)
}
Swift 3, 4
if let index = itemList.index(of: item) {
itemList.remove(at: index)
}
Swift 5.2
if let index = itemList.firstIndex(of: item) {
itemList.remove(at: index)
}
Updated for Swift 2+
Note that as of Swift 3 (or even 2), the extension below is no longer necessary as the global contains function has been made into a pair of extension method on Array, which allow you to do either of:
let a = [ 1, 2, 3, 4 ]
a.contains(2) // => true, only usable if Element : Equatable
a.contains { $0 < 1 } // => false
Historical Answer for Swift 1:
Use this extension: (updated to Swift 5.2)
extension Array {
func contains<T>(obj: T) -> Bool where T: Equatable {
return !self.filter({$0 as? T == obj}).isEmpty
}
}
Use as:
array.contains(1)
If you are checking if an instance of a custom class or struct is contained in an array, you'll need to implement the Equatable protocol before you can use .contains(myObject).
For example:
struct Cup: Equatable {
let filled:Bool
}
static func ==(lhs:Cup, rhs:Cup) -> Bool { // Implement Equatable
return lhs.filled == rhs.filled
}
then you can do:
cupArray.contains(myCup)
Tip: The == override should be at the global level, not within your class/struct
I used filter.
let results = elements.filter { el in el == 5 }
if results.count > 0 {
// any matching items are in results
} else {
// not found
}
If you want, you can compress that to
if elements.filter({ el in el == 5 }).count > 0 {
}
Hope that helps.
Update for Swift 2
Hurray for default implementations!
if elements.contains(5) {
// any matching items are in results
} else {
// not found
}
(Swift 3)
Check if an element exists in an array (fulfilling some criteria), and if so, proceed working with the first such element
If the intent is:
To check whether an element exist in an array (/fulfils some boolean criteria, not necessarily equality testing),
And if so, proceed and work with the first such element,
Then an alternative to contains(_:) as blueprinted Sequence is to first(where:) of Sequence:
let elements = [1, 2, 3, 4, 5]
if let firstSuchElement = elements.first(where: { $0 == 4 }) {
print(firstSuchElement) // 4
// ...
}
In this contrived example, its usage might seem silly, but it's very useful if querying arrays of non-fundamental element types for existence of any elements fulfilling some condition. E.g.
struct Person {
let age: Int
let name: String
init(_ age: Int, _ name: String) {
self.age = age
self.name = name
}
}
let persons = [Person(17, "Fred"), Person(16, "Susan"),
Person(19, "Hannah"), Person(18, "Sarah"),
Person(23, "Sam"), Person(18, "Jane")]
if let eligableDriver = persons.first(where: { $0.age >= 18 }) {
print("\(eligableDriver.name) can possibly drive the rental car in Sweden.")
// ...
} // Hannah can possibly drive the rental car in Sweden.
let daniel = Person(18, "Daniel")
if let sameAgeAsDaniel = persons.first(where: { $0.age == daniel.age }) {
print("\(sameAgeAsDaniel.name) is the same age as \(daniel.name).")
// ...
} // Sarah is the same age as Daniel.
Any chained operations using .filter { ... some condition }.first can favourably be replaced with first(where:). The latter shows intent better, and have performance advantages over possible non-lazy appliances of .filter, as these will pass the full array prior to extracting the (possible) first element passing the filter.
Check if an element exists in an array (fulfilling some criteria), and if so, remove the first such element
A comment below queries:
How can I remove the firstSuchElement from the array?
A similar use case to the one above is to remove the first element that fulfils a given predicate. To do so, the index(where:) method of Collection (which is readily available to array collection) may be used to find the index of the first element fulfilling the predicate, whereafter the index can be used with the remove(at:) method of Array to (possible; given that it exists) remove that element.
var elements = ["a", "b", "c", "d", "e", "a", "b", "c"]
if let indexOfFirstSuchElement = elements.index(where: { $0 == "c" }) {
elements.remove(at: indexOfFirstSuchElement)
print(elements) // ["a", "b", "d", "e", "a", "b", "c"]
}
Or, if you'd like to remove the element from the array and work with, apply Optional:s map(_:) method to conditionally (for .some(...) return from index(where:)) use the result from index(where:) to remove and capture the removed element from the array (within an optional binding clause).
var elements = ["a", "b", "c", "d", "e", "a", "b", "c"]
if let firstSuchElement = elements.index(where: { $0 == "c" })
.map({ elements.remove(at: $0) }) {
// if we enter here, the first such element have now been
// remove from the array
print(elements) // ["a", "b", "d", "e", "a", "b", "c"]
// and we may work with it
print(firstSuchElement) // c
}
Note that in the contrived example above the array members are simple value types (String instances), so using a predicate to find a given member is somewhat over-kill, as we might simply test for equality using the simpler index(of:) method as shown in #DogCoffee's answer. If applying the find-and-remove approach above to the Person example, however, using index(where:) with a predicate is appropriate (since we no longer test for equality but for fulfilling a supplied predicate).
An array that contains a property that equals to
yourArray.contains(where: {$0.propertyToCheck == value })
Returns boolean.
The simplest way to accomplish this is to use filter on the array.
let result = elements.filter { $0==5 }
result will have the found element if it exists and will be empty if the element does not exist. So simply checking if result is empty will tell you whether the element exists in the array. I would use the following:
if result.isEmpty {
// element does not exist in array
} else {
// element exists
}
Swift 4/5
Another way to achieve this is with the filter function
var elements = [1,2,3,4,5]
if let object = elements.filter({ $0 == 5 }).first {
print("found")
} else {
print("not found")
}
As of Swift 2.1 NSArrays have containsObjectthat can be used like so:
if myArray.containsObject(objectImCheckingFor){
//myArray has the objectImCheckingFor
}
Array
let elements = [1, 2, 3, 4, 5, 5]
Check elements presence
elements.contains(5) // true
Get elements index
elements.firstIndex(of: 5) // 4
elements.firstIndex(of: 10) // nil
Get element count
let results = elements.filter { element in element == 5 }
results.count // 2
Just in case anybody is trying to find if an indexPath is among the selected ones (like in a UICollectionView or UITableView cellForItemAtIndexPath functions):
var isSelectedItem = false
if let selectedIndexPaths = collectionView.indexPathsForSelectedItems() as? [NSIndexPath]{
if contains(selectedIndexPaths, indexPath) {
isSelectedItem = true
}
}
if user find particular array elements then use below code same as integer value.
var arrelemnts = ["sachin", "test", "test1", "test3"]
if arrelemnts.contains("test"){
print("found") }else{
print("not found") }
Here is my little extension I just wrote to check if my delegate array contains a delegate object or not (Swift 2). :) It Also works with value types like a charm.
extension Array
{
func containsObject(object: Any) -> Bool
{
if let anObject: AnyObject = object as? AnyObject
{
for obj in self
{
if let anObj: AnyObject = obj as? AnyObject
{
if anObj === anObject { return true }
}
}
}
return false
}
}
If you have an idea how to optimize this code, than just let me know.
Swift
If you are not using object then you can user this code for contains.
let elements = [ 10, 20, 30, 40, 50]
if elements.contains(50) {
print("true")
}
If you are using NSObject Class in swift. This variables is according to my requirement. you can modify for your requirement.
var cliectScreenList = [ATModelLeadInfo]()
var cliectScreenSelectedObject: ATModelLeadInfo!
This is for a same data type.
{ $0.user_id == cliectScreenSelectedObject.user_id }
If you want to AnyObject type.
{ "\($0.user_id)" == "\(cliectScreenSelectedObject.user_id)" }
Full condition
if cliectScreenSelected.contains( { $0.user_id == cliectScreenSelectedObject.user_id } ) == false {
cliectScreenSelected.append(cliectScreenSelectedObject)
print("Object Added")
} else {
print("Object already exists")
}
what about using a hash table for the job, like this?
first, creating a "hash map" generic function, extending the Sequence protocol.
extension Sequence where Element: Hashable {
func hashMap() -> [Element: Int] {
var dict: [Element: Int] = [:]
for (i, value) in self.enumerated() {
dict[value] = i
}
return dict
}
}
This extension will work as long as the items in the array conform to Hashable, like integers or strings, here is the usage...
let numbers = Array(0...50)
let hashMappedNumbers = numbers.hashMap()
let numToDetect = 35
let indexOfnumToDetect = hashMappedNumbers[numToDetect] // returns the index of the item and if all the elements in the array are different, it will work to get the index of the object!
print(indexOfnumToDetect) // prints 35
But for now, let's just focus in check if the element is in the array.
let numExists = indexOfnumToDetect != nil // if the key does not exist
means the number is not contained in the collection.
print(numExists) // prints true
Swift 4.2 +
You can easily verify your instance is an array or not by the following function.
func verifyIsObjectOfAnArray<T>(_ object: T) -> Bool {
if let _ = object as? [T] {
return true
}
return false
}
Even you can access it as follows. You will receive nil if the object wouldn't be an array.
func verifyIsObjectOfAnArray<T>(_ object: T) -> [T]? {
if let array = object as? [T] {
return array
}
return nil
}
You can add an extension for Array as such:
extension Array {
func contains<T>(_ object: T) -> Bool where T: Equatable {
!self.filter {$0 as? T == object }.isEmpty
}
}
This can be used as:
if myArray.contains(myItem) {
// code here
}