Related
I want to compare the elements of two arrays and check if they are equal.
I already tried various solutions but nothing really works.
I tried the solution from
How to compare two array of objects?
This is my object:
struct AccountBalance: Decodable {
let balance: Double
let currency: String
init(balance: Double, currency: String ) {
self.currency = currency
self.balance = balance
}
enum CodingKeys: String, CodingKey {
case currency = "Currency"
case balance = "Balance"
}
}
This is the code from the link I tried:
let result = zip(accountBalance, getsSaved).enumerate().filter() {
$1.0 == $1.1
}.map{$0.0}
But I get this error:
Closure tuple parameter '(offset: Int, element: (AccountBalance, AccountBalance))' does not support destructuring with implicit parameters
Array provides a function elementsEqual which is able to compare two arrays without explicitly conforming to Equatable:
let result = accountBalance.elementsEqual(getsSaved) {
$0.balance == $1.balance && $0.currency == $1.currency
}
Edit:
If you want to have the equality result regardless of the order of objects in the array then you can just add sort with each of the arrays.
let result = accountBalance.sorted { $0.balance < $1.balance }.elementsEqual(getsSaved.sorted { $0.balance < $1.balance }) {
$0.balance == $1.balance && $0.currency == $1.currency
}
I am guessing two arrays should be considered equal when they contain the same elements, regardless of ordering.
First, implement Equatable and Hashable.
I am using hashValue as an id so I can sort the arrays first.
Here is what your AccountBalance class should look like:
struct AccountBalance: Decodable, Equatable, Hashable {
// Important parts!
var hashValue: Int{
return balance.hashValue ^ currency.hashValue &* 1677619
}
static func == (lhs: AccountBalance, rhs: AccountBalance) -> Bool{
return lhs.balance == rhs.balance && lhs.currency == rhs.currency
}
}
Then create an algorithm that sorts the ararys and then check each elements by one by by if the contents are the same.
Here is the function that take use of Equatable and Hashable.
func isEqual(arr1: [AccountBalance], arr2: [AccountBalance]) -> Bool{
if arr1.count != arr1.count{
return false
}
let a = arr1.sorted(){
$0.hashValue > $1.hashValue
}
let b = arr2.sorted(){
$0.hashValue > $1.hashValue
}
let result = zip(a, b).enumerated().filter() {
$1.0 == $1.1
}.count
if result == a.count{
return true
}
return false
}
I will suggest you implement the accepted answer of the link you provided because it controls that both arrays are the same size and it orders them.
But if you want that your code works I solved like this:
In order to have control of the comparison your struct should implement the Equatable protocol and overload the operator ==
extension AccountBalance : Equatable {}
func ==(lhs: AccountBalance, rhs: AccountBalance) -> Bool {
return lhs.balance == rhs.balance && lhs.currency == rhs.currency
}
Then compare both arrays and check if contains false, if it does, one or more items in the array aren't the same.
let result = !zip(accountBalance, getsSaved).enumerated().map() {
$1.0 == $1.1
}.contains(false)
Hope it helps you
I'm not sure about what the rest of your code does, but making the parameters explicit does make XCode happy:
let result = zip(accountBalance, getsSaved).enumerated().filter() { (arg) -> Bool in
let (_, (balance1, balance2)) = arg
return balance1.balance == balance2.balance
}.map{ $0.0 }`
Tried (_, (balance1, balance2)) -> Bool in directly, but it wouldn't let me either
Sample with several arrays:
import Foundation
let arr1: [String?] = ["word", nil, "word3", "word4"]
let arr2: [Double?] = [1.01, 1.02, nil, 1.04]
let arr3: [Int?] = [nil, 2, 3, 4]
var tuple1: [(title: String, number: String, coord: String)] = []
let countArray = arr1.count
for element in 0..<countArray {
tuple1.append((arr1[element].map
{String($0)} ?? "", arr2[element].map
{String($0)} ?? "", arr3[element].map
{String($0)} ?? ""))
}
print(tuple1)
result of printing:
[(title: "word1", number: "1.01", coord: ""), (title: "", number: "1.02", coord: "2"), (title: "word3", number: "", coord: "3"), (title: "word4", number: "1.04", coord: "4")]
I'm trying to extend arrays that have dictionaries as it's elements, where the dictionaries' values are comparable. It's basically like an array of custom objects and sorting them by specific property values, only in this case, it's dictionaries, not class instances.
One could see the data structure as Array<Dictionary<Key: Hashable, Value: Comparable>>, although the previous code is unsupported..
I think it comes close to working, but fails for various reasons.
My first attempts were
extension ArrayLiteralConvertible where Element == Dictionary<String, Comparable> {
func sortByKey<T: Comparable>(key: String, ascending: Bool) -> [[String: T]]? {
if let dictType = self as? [[String: T]] {
return ascending ? dictType.sort { return $0[key] < $1[key] } : dictType.sort { return $0[key] > $1[key] }
} else {
return nil
}
}
}
This works in the declaration, but when in use, it throws the error
Dictionary is not convertible to '[String: Int]'
Another attempt:
extension SequenceType where Generator.Element == [String: AnyObject] {
func sortByKey<T: Comparable>(key: String, ascending: Bool) -> [[String: T]]? {
if let dictType = self as? [[String: T]] {
return ascending ? dictType.sort { return $0[key] < $1[key] } : dictType.sort { return $0[key] > $1[key] }
} else {
return nil
}
}
}
gives back
Argument type "[[String: _]]?" does not conform to expected type 'Any' (aka 'protocol<>')
How could I make this work?
ADDED
I made-do with the following, but it's constrained to a specific type, so I have to declare separately for other types like String..
extension ArrayLiteralConvertible where Element == [String: Int] {
func sortByKey(key: String, ascending: Bool) -> [[String: Int]] {
let arraySelf = self as! [[String: Int]]
return ascending ? arraySelf.sort { return $0[key] < $1[key] } : arraySelf.sort { return $0[key] > $1[key] }
}
}
Another alternative:
func sortArray<T: Comparable>(array: [[String: T]], byKey key: String, ascending: Bool) -> [[String: T]] {
return ascending ? array.sort { return $0[key] < $1[key] } : array.sort { return $0[key] > $1[key] }
}
I think you did pretty well. I don't know if you'll like this any better:
func sortedArrayOfDictionaries<T:Comparable>(arr:[[NSObject:T]], byKey key:NSObject) -> [[NSObject:T]] {
return arr.sort({ (d1, d2) -> Bool in
return d1[key] < d2[key]
})
}
To use it, you must cast your initial array so that the key is of type NSObject:
let arr : [[NSObject:Int]] = [["hey":2], ["hey":1]]
let arr2 = sortedArrayOfDictionaries(arr, byKey: "hey")
I arrived at that independently, but on inspection it's not significantly different from your second solution. The only difference, really, is that we are not restricted to a string key.
extension Array {
func removeObject<T where T : Equatable>(object: T) {
var index = find(self, object)
self.removeAtIndex(index)
}
}
However, I get an error on var index = find(self, object)
'T' is not convertible to 'T'
I also tried with this method signature: func removeObject(object: AnyObject), however, I get the same error:
'AnyObject' is not convertible to 'T'
What is the proper way to do this?
As of Swift 2, this can be achieved with a protocol extension method.
removeObject() is defined as a method on all types conforming
to RangeReplaceableCollectionType (in particular on Array) if
the elements of the collection are Equatable:
extension RangeReplaceableCollectionType where Generator.Element : Equatable {
// Remove first collection element that is equal to the given `object`:
mutating func removeObject(object : Generator.Element) {
if let index = self.indexOf(object) {
self.removeAtIndex(index)
}
}
}
Example:
var ar = [1, 2, 3, 2]
ar.removeObject(2)
print(ar) // [1, 3, 2]
Update for Swift 2 / Xcode 7 beta 2: As Airspeed Velocity noticed
in the comments, it is now actually possible to write a method on a generic type that is more restrictive on the template, so the method
could now actually be defined as an extension of Array:
extension Array where Element : Equatable {
// ... same method as above ...
}
The protocol extension still has the advantage of being applicable to
a larger set of types.
Update for Swift 3:
extension Array where Element: Equatable {
// Remove first collection element that is equal to the given `object`:
mutating func remove(object: Element) {
if let index = index(of: object) {
remove(at: index)
}
}
}
Update for Swift 5:
extension Array where Element: Equatable {
/// Remove first collection element that is equal to the given `object` or `element`:
mutating func remove(element: Element) {
if let index = firstIndex(of: element) {
remove(at: index)
}
}
}
You cannot write a method on a generic type that is more restrictive on the template.
NOTE: as of Swift 2.0, you can now write methods that are more restrictive on the template. If you have upgraded your code to 2.0, see other answers further down for new options to implement this using extensions.
The reason you get the error 'T' is not convertible to 'T' is that you are actually defining a new T in your method that is not related at all to the original T. If you wanted to use T in your method, you can do so without specifying it on your method.
The reason that you get the second error 'AnyObject' is not convertible to 'T' is that all possible values for T are not all classes. For an instance to be converted to AnyObject, it must be a class (it cannot be a struct, enum, etc.).
Your best bet is to make it a function that accepts the array as an argument:
func removeObject<T : Equatable>(object: T, inout fromArray array: [T]) {
}
Or instead of modifying the original array, you can make your method more thread safe and reusable by returning a copy:
func arrayRemovingObject<T : Equatable>(object: T, fromArray array: [T]) -> [T] {
}
As an alternative that I don't recommend, you can have your method fail silently if the type stored in the array cannot be converted to the the methods template (that is equatable). (For clarity, I am using U instead of T for the method's template):
extension Array {
mutating func removeObject<U: Equatable>(object: U) {
var index: Int?
for (idx, objectToCompare) in enumerate(self) {
if let to = objectToCompare as? U {
if object == to {
index = idx
}
}
}
if(index != nil) {
self.removeAtIndex(index!)
}
}
}
var list = [1,2,3]
list.removeObject(2) // Successfully removes 2 because types matched
list.removeObject("3") // fails silently to remove anything because the types don't match
list // [1, 3]
Edit To overcome the silent failure you can return the success as a bool:
extension Array {
mutating func removeObject<U: Equatable>(object: U) -> Bool {
for (idx, objectToCompare) in self.enumerate() { //in old swift use enumerate(self)
if let to = objectToCompare as? U {
if object == to {
self.removeAtIndex(idx)
return true
}
}
}
return false
}
}
var list = [1,2,3,2]
list.removeObject(2)
list
list.removeObject(2)
list
briefly and concisely:
func removeObject<T : Equatable>(object: T, inout fromArray array: [T])
{
var index = find(array, object)
array.removeAtIndex(index!)
}
After reading all the above, to my mind the best answer is:
func arrayRemovingObject<U: Equatable>(object: U, # fromArray:[U]) -> [U] {
return fromArray.filter { return $0 != object }
}
Sample:
var myArray = ["Dog", "Cat", "Ant", "Fish", "Cat"]
myArray = arrayRemovingObject("Cat", fromArray:myArray )
Swift 2 (xcode 7b4) array extension:
extension Array where Element: Equatable {
func arrayRemovingObject(object: Element) -> [Element] {
return filter { $0 != object }
}
}
Sample:
var myArray = ["Dog", "Cat", "Ant", "Fish", "Cat"]
myArray = myArray.arrayRemovingObject("Cat" )
Swift 3.1 update
Came back to this now that Swift 3.1 is out. Below is an extension which provides exhaustive, fast, mutating and creating variants.
extension Array where Element:Equatable {
public mutating func remove(_ item:Element ) {
var index = 0
while index < self.count {
if self[index] == item {
self.remove(at: index)
} else {
index += 1
}
}
}
public func array( removing item:Element ) -> [Element] {
var result = self
result.remove( item )
return result
}
}
Samples:
// Mutation...
var array1 = ["Cat", "Dog", "Turtle", "Cat", "Fish", "Cat"]
array1.remove("Cat")
print(array1) // ["Dog", "Turtle", "Socks"]
// Creation...
let array2 = ["Cat", "Dog", "Turtle", "Cat", "Fish", "Cat"]
let array3 = array2.array(removing:"Cat")
print(array3) // ["Dog", "Turtle", "Fish"]
With protocol extensions you can do this,
extension Array where Element: Equatable {
mutating func remove(object: Element) {
if let index = indexOf({ $0 == object }) {
removeAtIndex(index)
}
}
}
Same functionality for classes,
Swift 2
extension Array where Element: AnyObject {
mutating func remove(object: Element) {
if let index = indexOf({ $0 === object }) {
removeAtIndex(index)
}
}
}
Swift 3
extension Array where Element: AnyObject {
mutating func remove(object: Element) {
if let index = index(where: { $0 === object }) {
remove(at: index)
}
}
}
But if a class implements Equatable it becomes ambiguous and the compiler gives an throws an error.
With using protocol extensions in swift 2.0
extension _ArrayType where Generator.Element : Equatable{
mutating func removeObject(object : Self.Generator.Element) {
while let index = self.indexOf(object){
self.removeAtIndex(index)
}
}
}
what about to use filtering? the following works quite well even with [AnyObject].
import Foundation
extension Array {
mutating func removeObject<T where T : Equatable>(obj: T) {
self = self.filter({$0 as? T != obj})
}
}
Maybe I didn't understand the question.
Why wouldn't this work?
import Foundation
extension Array where Element: Equatable {
mutating func removeObject(object: Element) {
if let index = self.firstIndex(of: object) {
self.remove(at: index)
}
}
}
var testArray = [1,2,3,4,5,6,7,8,9,0]
testArray.removeObject(object: 6)
let newArray = testArray
var testArray2 = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]
testArray2.removeObject(object: "6")
let newArray2 = testArray2
No need to extend:
var ra = [7, 2, 5, 5, 4, 5, 3, 4, 2]
print(ra) // [7, 2, 5, 5, 4, 5, 3, 4, 2]
ra.removeAll(where: { $0 == 5 })
print(ra) // [7, 2, 4, 3, 4, 2]
if let i = ra.firstIndex(of: 4) {
ra.remove(at: i)
}
print(ra) // [7, 2, 3, 4, 2]
if let j = ra.lastIndex(of: 2) {
ra.remove(at: j)
}
print(ra) // [7, 2, 3, 4]
There is another possibility of removing an item from an array without having possible unsafe usage, as the generic type of the object to remove cannot be the same as the type of the array. Using optionals is also not the perfect way to go as they are very slow. You could therefore use a closure like it is already used when sorting an array for example.
//removes the first item that is equal to the specified element
mutating func removeFirst(element: Element, equality: (Element, Element) -> Bool) -> Bool {
for (index, item) in enumerate(self) {
if equality(item, element) {
self.removeAtIndex(index)
return true
}
}
return false
}
When you extend the Array class with this function you can remove elements by doing the following:
var array = ["Apple", "Banana", "Strawberry"]
array.removeFirst("Banana") { $0 == $1 } //Banana is now removed
However you could even remove an element only if it has the same memory address (only for classes conforming to AnyObject protocol, of course):
let date1 = NSDate()
let date2 = NSDate()
var array = [date1, date2]
array.removeFirst(NSDate()) { $0 === $1 } //won't do anything
array.removeFirst(date1) { $0 === $1 } //array now contains only 'date2'
The good thing is, that you can specify the parameter to compare. For example when you have an array of arrays, you can specify the equality closure as { $0.count == $1.count } and the first array having the same size as the one to remove is removed from the array.
You could even shorten the function call by having the function as mutating func removeFirst(equality: (Element) -> Bool) -> Bool, then replace the if-evaluation with equality(item) and call the function by array.removeFirst({ $0 == "Banana" }) for example.
Using indexOf instead of a for or enumerate:
extension Array where Element: Equatable {
mutating func removeElement(element: Element) -> Element? {
if let index = indexOf(element) {
return removeAtIndex(index)
}
return nil
}
mutating func removeAllOccurrencesOfElement(element: Element) -> Int {
var occurrences = 0
while true {
if let index = indexOf(element) {
removeAtIndex(index)
occurrences++
} else {
return occurrences
}
}
}
}
I finally ended up with following code.
extension Array where Element: Equatable {
mutating func remove<Element: Equatable>(item: Element) -> Array {
self = self.filter { $0 as? Element != item }
return self
}
}
Your problem is T is not related to the type of your array in anyway for example you could have
var array = [1,2,3,4,5,6]
array.removeObject(object:"four")
"six" is Equatable, but its not a type that can be compared to Integer, if you change it to
var array = [1,2,3,4,5,6]
extension Array where Element : Equatable {
mutating func removeObject(object: Element) {
filter { $0 != object }
}
}
array.removeObject(object:"four")
it now produces an error on calling removeObject for the obvious reason its not an array of strings, to remove 4 you can just
array.removeObject(object:4)
Other problem you have is its a self modifying struct so the method has to be labeled as so and your reference to it at the top has to be a var
Implementation in Swift 2:
extension Array {
mutating func removeObject<T: Equatable>(object: T) -> Bool {
var index: Int?
for (idx, objectToCompare) in self.enumerate() {
if let toCompare = objectToCompare as? T {
if toCompare == object {
index = idx
break
}
}
}
if(index != nil) {
self.removeAtIndex(index!)
return true
} else {
return false
}
}
}
I was able to get it working with:
extension Array {
mutating func removeObject<T: Equatable>(object: T) {
var index: Int?
for (idx, objectToCompare) in enumerate(self) {
let to = objectToCompare as T
if object == to {
index = idx
}
}
if(index) {
self.removeAtIndex(index!)
}
}
}
extension Array {
func removeObject<T where T : Equatable>(object: T) {
var index = find(self, object)
self.removeAtIndex(index)
}
}
However, I get an error on var index = find(self, object)
'T' is not convertible to 'T'
I also tried with this method signature: func removeObject(object: AnyObject), however, I get the same error:
'AnyObject' is not convertible to 'T'
What is the proper way to do this?
As of Swift 2, this can be achieved with a protocol extension method.
removeObject() is defined as a method on all types conforming
to RangeReplaceableCollectionType (in particular on Array) if
the elements of the collection are Equatable:
extension RangeReplaceableCollectionType where Generator.Element : Equatable {
// Remove first collection element that is equal to the given `object`:
mutating func removeObject(object : Generator.Element) {
if let index = self.indexOf(object) {
self.removeAtIndex(index)
}
}
}
Example:
var ar = [1, 2, 3, 2]
ar.removeObject(2)
print(ar) // [1, 3, 2]
Update for Swift 2 / Xcode 7 beta 2: As Airspeed Velocity noticed
in the comments, it is now actually possible to write a method on a generic type that is more restrictive on the template, so the method
could now actually be defined as an extension of Array:
extension Array where Element : Equatable {
// ... same method as above ...
}
The protocol extension still has the advantage of being applicable to
a larger set of types.
Update for Swift 3:
extension Array where Element: Equatable {
// Remove first collection element that is equal to the given `object`:
mutating func remove(object: Element) {
if let index = index(of: object) {
remove(at: index)
}
}
}
Update for Swift 5:
extension Array where Element: Equatable {
/// Remove first collection element that is equal to the given `object` or `element`:
mutating func remove(element: Element) {
if let index = firstIndex(of: element) {
remove(at: index)
}
}
}
You cannot write a method on a generic type that is more restrictive on the template.
NOTE: as of Swift 2.0, you can now write methods that are more restrictive on the template. If you have upgraded your code to 2.0, see other answers further down for new options to implement this using extensions.
The reason you get the error 'T' is not convertible to 'T' is that you are actually defining a new T in your method that is not related at all to the original T. If you wanted to use T in your method, you can do so without specifying it on your method.
The reason that you get the second error 'AnyObject' is not convertible to 'T' is that all possible values for T are not all classes. For an instance to be converted to AnyObject, it must be a class (it cannot be a struct, enum, etc.).
Your best bet is to make it a function that accepts the array as an argument:
func removeObject<T : Equatable>(object: T, inout fromArray array: [T]) {
}
Or instead of modifying the original array, you can make your method more thread safe and reusable by returning a copy:
func arrayRemovingObject<T : Equatable>(object: T, fromArray array: [T]) -> [T] {
}
As an alternative that I don't recommend, you can have your method fail silently if the type stored in the array cannot be converted to the the methods template (that is equatable). (For clarity, I am using U instead of T for the method's template):
extension Array {
mutating func removeObject<U: Equatable>(object: U) {
var index: Int?
for (idx, objectToCompare) in enumerate(self) {
if let to = objectToCompare as? U {
if object == to {
index = idx
}
}
}
if(index != nil) {
self.removeAtIndex(index!)
}
}
}
var list = [1,2,3]
list.removeObject(2) // Successfully removes 2 because types matched
list.removeObject("3") // fails silently to remove anything because the types don't match
list // [1, 3]
Edit To overcome the silent failure you can return the success as a bool:
extension Array {
mutating func removeObject<U: Equatable>(object: U) -> Bool {
for (idx, objectToCompare) in self.enumerate() { //in old swift use enumerate(self)
if let to = objectToCompare as? U {
if object == to {
self.removeAtIndex(idx)
return true
}
}
}
return false
}
}
var list = [1,2,3,2]
list.removeObject(2)
list
list.removeObject(2)
list
briefly and concisely:
func removeObject<T : Equatable>(object: T, inout fromArray array: [T])
{
var index = find(array, object)
array.removeAtIndex(index!)
}
After reading all the above, to my mind the best answer is:
func arrayRemovingObject<U: Equatable>(object: U, # fromArray:[U]) -> [U] {
return fromArray.filter { return $0 != object }
}
Sample:
var myArray = ["Dog", "Cat", "Ant", "Fish", "Cat"]
myArray = arrayRemovingObject("Cat", fromArray:myArray )
Swift 2 (xcode 7b4) array extension:
extension Array where Element: Equatable {
func arrayRemovingObject(object: Element) -> [Element] {
return filter { $0 != object }
}
}
Sample:
var myArray = ["Dog", "Cat", "Ant", "Fish", "Cat"]
myArray = myArray.arrayRemovingObject("Cat" )
Swift 3.1 update
Came back to this now that Swift 3.1 is out. Below is an extension which provides exhaustive, fast, mutating and creating variants.
extension Array where Element:Equatable {
public mutating func remove(_ item:Element ) {
var index = 0
while index < self.count {
if self[index] == item {
self.remove(at: index)
} else {
index += 1
}
}
}
public func array( removing item:Element ) -> [Element] {
var result = self
result.remove( item )
return result
}
}
Samples:
// Mutation...
var array1 = ["Cat", "Dog", "Turtle", "Cat", "Fish", "Cat"]
array1.remove("Cat")
print(array1) // ["Dog", "Turtle", "Socks"]
// Creation...
let array2 = ["Cat", "Dog", "Turtle", "Cat", "Fish", "Cat"]
let array3 = array2.array(removing:"Cat")
print(array3) // ["Dog", "Turtle", "Fish"]
With protocol extensions you can do this,
extension Array where Element: Equatable {
mutating func remove(object: Element) {
if let index = indexOf({ $0 == object }) {
removeAtIndex(index)
}
}
}
Same functionality for classes,
Swift 2
extension Array where Element: AnyObject {
mutating func remove(object: Element) {
if let index = indexOf({ $0 === object }) {
removeAtIndex(index)
}
}
}
Swift 3
extension Array where Element: AnyObject {
mutating func remove(object: Element) {
if let index = index(where: { $0 === object }) {
remove(at: index)
}
}
}
But if a class implements Equatable it becomes ambiguous and the compiler gives an throws an error.
With using protocol extensions in swift 2.0
extension _ArrayType where Generator.Element : Equatable{
mutating func removeObject(object : Self.Generator.Element) {
while let index = self.indexOf(object){
self.removeAtIndex(index)
}
}
}
what about to use filtering? the following works quite well even with [AnyObject].
import Foundation
extension Array {
mutating func removeObject<T where T : Equatable>(obj: T) {
self = self.filter({$0 as? T != obj})
}
}
Maybe I didn't understand the question.
Why wouldn't this work?
import Foundation
extension Array where Element: Equatable {
mutating func removeObject(object: Element) {
if let index = self.firstIndex(of: object) {
self.remove(at: index)
}
}
}
var testArray = [1,2,3,4,5,6,7,8,9,0]
testArray.removeObject(object: 6)
let newArray = testArray
var testArray2 = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]
testArray2.removeObject(object: "6")
let newArray2 = testArray2
No need to extend:
var ra = [7, 2, 5, 5, 4, 5, 3, 4, 2]
print(ra) // [7, 2, 5, 5, 4, 5, 3, 4, 2]
ra.removeAll(where: { $0 == 5 })
print(ra) // [7, 2, 4, 3, 4, 2]
if let i = ra.firstIndex(of: 4) {
ra.remove(at: i)
}
print(ra) // [7, 2, 3, 4, 2]
if let j = ra.lastIndex(of: 2) {
ra.remove(at: j)
}
print(ra) // [7, 2, 3, 4]
There is another possibility of removing an item from an array without having possible unsafe usage, as the generic type of the object to remove cannot be the same as the type of the array. Using optionals is also not the perfect way to go as they are very slow. You could therefore use a closure like it is already used when sorting an array for example.
//removes the first item that is equal to the specified element
mutating func removeFirst(element: Element, equality: (Element, Element) -> Bool) -> Bool {
for (index, item) in enumerate(self) {
if equality(item, element) {
self.removeAtIndex(index)
return true
}
}
return false
}
When you extend the Array class with this function you can remove elements by doing the following:
var array = ["Apple", "Banana", "Strawberry"]
array.removeFirst("Banana") { $0 == $1 } //Banana is now removed
However you could even remove an element only if it has the same memory address (only for classes conforming to AnyObject protocol, of course):
let date1 = NSDate()
let date2 = NSDate()
var array = [date1, date2]
array.removeFirst(NSDate()) { $0 === $1 } //won't do anything
array.removeFirst(date1) { $0 === $1 } //array now contains only 'date2'
The good thing is, that you can specify the parameter to compare. For example when you have an array of arrays, you can specify the equality closure as { $0.count == $1.count } and the first array having the same size as the one to remove is removed from the array.
You could even shorten the function call by having the function as mutating func removeFirst(equality: (Element) -> Bool) -> Bool, then replace the if-evaluation with equality(item) and call the function by array.removeFirst({ $0 == "Banana" }) for example.
Using indexOf instead of a for or enumerate:
extension Array where Element: Equatable {
mutating func removeElement(element: Element) -> Element? {
if let index = indexOf(element) {
return removeAtIndex(index)
}
return nil
}
mutating func removeAllOccurrencesOfElement(element: Element) -> Int {
var occurrences = 0
while true {
if let index = indexOf(element) {
removeAtIndex(index)
occurrences++
} else {
return occurrences
}
}
}
}
I finally ended up with following code.
extension Array where Element: Equatable {
mutating func remove<Element: Equatable>(item: Element) -> Array {
self = self.filter { $0 as? Element != item }
return self
}
}
Your problem is T is not related to the type of your array in anyway for example you could have
var array = [1,2,3,4,5,6]
array.removeObject(object:"four")
"six" is Equatable, but its not a type that can be compared to Integer, if you change it to
var array = [1,2,3,4,5,6]
extension Array where Element : Equatable {
mutating func removeObject(object: Element) {
filter { $0 != object }
}
}
array.removeObject(object:"four")
it now produces an error on calling removeObject for the obvious reason its not an array of strings, to remove 4 you can just
array.removeObject(object:4)
Other problem you have is its a self modifying struct so the method has to be labeled as so and your reference to it at the top has to be a var
Implementation in Swift 2:
extension Array {
mutating func removeObject<T: Equatable>(object: T) -> Bool {
var index: Int?
for (idx, objectToCompare) in self.enumerate() {
if let toCompare = objectToCompare as? T {
if toCompare == object {
index = idx
break
}
}
}
if(index != nil) {
self.removeAtIndex(index!)
return true
} else {
return false
}
}
}
I was able to get it working with:
extension Array {
mutating func removeObject<T: Equatable>(object: T) {
var index: Int?
for (idx, objectToCompare) in enumerate(self) {
let to = objectToCompare as T
if object == to {
index = idx
}
}
if(index) {
self.removeAtIndex(index!)
}
}
}
I want to extend Array class so that it can know whether it is sorted (ascending) or not. I want to add a computed property called isSorted. How can I state the elements of the Array to be comparable?
My current implementation in Playground
extension Array {
var isSorted: Bool {
for i in 1..self.count {
if self[i-1] > self[i] { return false }
}
return true
}
}
// The way I want to get the computed property
[1, 1, 2, 3, 4, 5, 6, 7, 8].isSorted //= true
[2, 1, 3, 8, 5, 6, 7, 4, 8].isSorted //= false
The error Could not find an overload for '>' that accepts the supplied arguments
Of course, I still got an error because Swift doesn't know how to compare the elements. How can I implement this extension in Swift? Or am I doing something wrong here?
The alternative solution to a free function is to do what Swift's built-in Array.sort and Array.sorted methods do, and require that you pass a suitable comparator to the method:
extension Array {
func isSorted(isOrderedBefore: (T, T) -> Bool) -> Bool {
for i in 1..<self.count {
if !isOrderedBefore(self[i-1], self[i]) {
return false
}
}
return true
}
}
[1, 5, 3].isSorted(<) // false
[1, 5, 10].isSorted(<) // true
[3.5, 2.1, -5.4].isSorted(>) // true
In Swift 2.0 you can now extend protocols!
extension CollectionType where Generator.Element: Comparable {
public var isSorted: Bool {
var previousIndex = startIndex
var currentIndex = startIndex.successor()
while currentIndex != endIndex {
if self[previousIndex] > self[currentIndex] {
return false
}
previousIndex = currentIndex
currentIndex = currentIndex.successor()
}
return true
}
}
[1, 2, 3, 4].isSorted // true
["a", "b", "c", "e"].isSorted // true
["b", "a", "c", "e"].isSorted // false
[/* Anything not implementing `Comparable` */].isSorted // <~~ Type-error
Note that because we're using Indexable.Index instead of a simple Int as an index we have to use a while-loop instead, which looks a bit less pretty and clear.
In Swift 4.2 and later you can cobble together allSatisfy and zip with some sequence slicing:
extension Array where Element: Comparable {
func isAscending() -> Bool {
return zip(self, self.dropFirst()).allSatisfy(<=)
}
func isDescending() -> Bool {
return zip(self, self.dropFirst()).allSatisfy(>=)
}
}
Actually, you can extend the Sequence protocol for a more generic solution:
extension Sequence {
func isSorted(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> Bool {
var iterator = makeIterator()
guard var previous = iterator.next() else {
// Sequence is empty
return true
}
while let current = iterator.next() {
guard try areInIncreasingOrder(previous, current) else {
return false
}
previous = current
}
return true
}
}
extension Sequence where Element : Comparable {
func isSorted() -> Bool {
return isSorted(by: <)
}
}
Adaptation, a solution that will work in Swift 4
extension Array where Iterator.Element: Comparable {
func isSorted(isOrderedBefore: (Iterator.Element, Iterator.Element) -> Bool) -> Bool {
for i in 1 ..< self.count {
if isOrderedBefore(self[i], self[i-1]) {
return false
}
}
return true
}
}
The most flexible solution to me is a combination of NSAddict's and Wes Campaigne's answer. I.e. combine the advantage of being able to extend protocols and to pass comparator functions as arguments. This eliminates the restrictions both to use it only with arrays and to constrain it to elements conforming to Comparable protocol.
extension CollectionType
{
func isSorted(isOrderedBefore: (Generator.Element, Generator.Element) -> Bool) -> Bool
{
var previousIndex = startIndex
var currentIndex = startIndex.successor()
while currentIndex != endIndex
{
if isOrderedBefore(self[previousIndex], self[currentIndex]) == false
{
return false
}
previousIndex = currentIndex
currentIndex = currentIndex.successor()
}
return true
}
}
This can be used on any Collection type and sorting criteria can be defined according to your needs.
Here is a solution in Swift 4 that won't crash when self.count is equal or less than 1:
extension Array where Element: Comparable {
func isSorted(by isOrderedBefore: (Element, Element) -> Bool) -> Bool {
for i in stride(from: 1, to: self.count, by: 1) {
if !isOrderedBefore(self[i-1], self[i]) {
return false
}
}
return true
}
}
This snippet supposes that an array of 1 or 0 elements is already sorted.
The reason to start with 1 in the for-loop range is: In case self.count <= 1, the loop will be skipped, a small performance increase. Using stride instead of ..< avoids the crash when the upper bound is < than the lower bound of a range.
Here are some examples:
[1, 2, 3].isSorted(by: >) // true
[3, 2, 2].isSorted(by: >=) // true
[1, 4, 7].isSorted(by: {x, y in
return x + 2 < y * y
}) // true
let a: [Int] = [1]
a.isSorted(by: <) // true
let b: [Int] = []
b.isSorted(by: >) // true
Summarising previous answers there is a smallest universal Array extension to check:
extension Array {
func isSorted(_ predicate: (Element, Element) throws -> Bool) -> Bool {
guard count > 1 else { return true }
return (try? zip(self, self.dropFirst()).allSatisfy(predicate)) == true
}
}
// Standard types
[1, 2, 3, 4, 5].isSorted(<) // true
[1, 2, 10, 4, 5].isSorted(<) // false
[10, 5, 4, 3, 1].isSorted(>) // true
[10, 20, 4, 3, 1].isSorted(>) // false
// Custom types
struct MyStruct {
let i: Int
}
let items = [MyStruct(i: 1), MyStruct(i: 2), MyStruct(i: 3), MyStruct(i: 10)]
let sorted = items.isSorted { $0.i < $1.i } // true
Other answers have incorporated allSatisfy, but not adjacentPairs, which makes this so easy that this extension may not be warranted.
import Algorithms
public extension Sequence where Element: Comparable {
/// Whether the elements of the sequence are in order.
#inlinable var isSorted: Bool { adjacentPairs().allSatisfy(<=) }
}
let random = Int.random(in: 1...(.max))
let stride = stride(from: -random, through: random, by: random)
XCTAssert(stride.isSorted)
XCTAssertFalse(stride.reversed().isSorted)
However, it's very common to want to use a property of the elements for this, not the elements themselves:
import Algorithms
public extension Sequence {
/// Whether the elements of this sequence are sorted by a common `Comparable` value.
#inlinable func isSorted<Comparable: Swift.Comparable>(
by comparable: (Element) throws -> Comparable
) rethrows -> Bool {
try isSorted(by: comparable, <=)
}
/// Whether the elements of this sequence are sorted by a common `Comparable` value,
/// and sorting closure.
#inlinable func isSorted<Comparable: Swift.Comparable>(
by comparable: (Element) throws -> Comparable,
_ areInIncreasingOrder: (Comparable, Comparable) throws -> Bool
) rethrows -> Bool {
try adjacentPairs().allSatisfy {
try areInIncreasingOrder(comparable($0), comparable($1))
}
}
}
struct TypeWithComparable {
let comparable: Int
}
let random = Int.random(in: 1...(.max))
let stride =
stride(from: -random, through: random, by: random)
.lazy.map(TypeWithComparable.init)
XCTAssert(stride.isSorted(by: \.comparable))
XCTAssert(stride.reversed().isSorted(by: \.comparable, >=))
If you want simple function without arguments, like sort() or sorted() in Swift:
extension Array where Element : Comparable {
func isSorted() -> Bool {
guard self.count > 1 else {
return true
}
for i in 1..<self.count {
if self[i-1] > self[i] {
return false
}
}
return true
}
}
The generic function, zip(), can provide a shortcut for implementation.
extension Collection where Element: Comparable {
var isSorted: Bool {
guard count > 1 else {
return true
}
let pairs = zip(prefix(count - 1), suffix(count - 1))
return !pairs.contains { previous, next in
previous > next
}
}
}
[0, 1, 1, 2].isSorted // true
[0, 2, 2, 1].isSorted // false
#kAzec's answer seems to not working when elements are equal. This is because areInIncreasingOrder(a, a) must be false according to the documentation.
The following should work fine.
extension Sequence {
func isSorted(by areInIncreasingOrder: (Element, Element) throws -> Bool)
rethrows -> Bool {
var it = makeIterator()
guard var previous = it.next() else { return true }
while let current = it.next() {
if try !areInIncreasingOrder(previous, current) &&
areInIncreasingOrder(current, previous) {
return false
}
previous = current
}
return true
}
}
extension Sequence where Element: Comparable {
func isSorted() -> Bool {
return isSorted(by: <)
}
}
Just for fun. This supports duplicated elements that are equal as well:
extension Sequence {
var neighbors: Zip2Sequence<Self, DropFirstSequence<Self>> {
zip(self, dropFirst())
}
func isSorted<T: Comparable>(_ predicate: (Element) throws -> T) rethrows -> Bool {
try isSorted(predicate, by: <)
}
func isSorted<T: Comparable>(
_ predicate: (Element) throws -> T,
by areInIncreasingOrder: (T, T) throws -> Bool
) rethrows -> Bool {
try neighbors.allSatisfy {
try areInIncreasingOrder(predicate($0), predicate($1)) ||
predicate($0) == predicate($1)
}
}
}
extension Sequence where Element: Comparable {
var isSorted: Bool { isSorted(by: <) }
func isSorted(
by areInIncreasingOrder: (Element, Element) throws -> Bool
) rethrows -> Bool {
try neighbors.allSatisfy {
try areInIncreasingOrder($0, $1) || $0 == $1
}
}
}
Usage:
[1,2,2,3].isSorted // true
[3,2,2,1].isSorted(by: >) // true
struct Test {
let id: Int
}
[1,2,2,3].map(Test.init).isSorted(\.id) // true
[3,2,2,1].map(Test.init).isSorted(\.id, by: >) // true