Consider the array [1,2,3,4]. How can I rearrange the array item to new position.
For example:
put 3 into position 4 [1,2,4,3]
put 4 in to position 1 [4,1,2,3]
put 2 into position 3 [1,3,2,4].
Swift 3.0+:
let element = arr.remove(at: 3)
arr.insert(element, at: 2)
and in function form:
func rearrange<T>(array: Array<T>, fromIndex: Int, toIndex: Int) -> Array<T>{
var arr = array
let element = arr.remove(at: fromIndex)
arr.insert(element, at: toIndex)
return arr
}
Swift 2.0:
This puts 3 into position 4.
let element = arr.removeAtIndex(3)
arr.insert(element, atIndex: 2)
You can even make a general function:
func rearrange<T>(array: Array<T>, fromIndex: Int, toIndex: Int) -> Array<T>{
var arr = array
let element = arr.removeAtIndex(fromIndex)
arr.insert(element, atIndex: toIndex)
return arr
}
The var arr is needed here, because you can't mutate the input parameter without specifying it to be in-out. In our case however we get a pure functions with no side effects, which is a lot easier to reason with, in my opinion.
You could then call it like this:
let arr = [1,2,3,4]
rearrange(arr, fromIndex: 2, toIndex: 0) //[3,1,2,4]
All great answers! Here's a more complete Swift 5 solution with performance in mind and bonus for benchmark and GIF fans. ✌️
extension Array where Element: Equatable
{
mutating func move(_ element: Element, to newIndex: Index) {
if let oldIndex: Int = self.firstIndex(of: element) { self.move(from: oldIndex, to: newIndex) }
}
}
extension Array
{
mutating func move(from oldIndex: Index, to newIndex: Index) {
// Don't work for free and use swap when indices are next to each other - this
// won't rebuild array and will be super efficient.
if oldIndex == newIndex { return }
if abs(newIndex - oldIndex) == 1 { return self.swapAt(oldIndex, newIndex) }
self.insert(self.remove(at: oldIndex), at: newIndex)
}
}
edit/update: Swift 3.x
extension RangeReplaceableCollection where Indices: Equatable {
mutating func rearrange(from: Index, to: Index) {
precondition(from != to && indices.contains(from) && indices.contains(to), "invalid indices")
insert(remove(at: from), at: to)
}
}
var numbers = [1,2,3,4]
numbers.rearrange(from: 1, to: 2)
print(numbers) // [1, 3, 2, 4]
nice tip from Leo.
for Swift 3...5.5:
extension Array {
mutating func rearrange(from: Int, to: Int) {
insert(remove(at: from), at: to)
}
}
var myArray = [1,2,3,4]
myArray.rearrange(from: 1, to: 2)
print(myArray)
var arr = ["one", "two", "three", "four", "five"]
// Swap elements at index: 2 and 3
print(arr)
arr.swapAt(2, 3)
print(arr)
Swift 5
extension Array where Element: Equatable {
mutating func move(_ item: Element, to newIndex: Index) {
if let index = index(of: item) {
move(at: index, to: newIndex)
}
}
mutating func bringToFront(item: Element) {
move(item, to: 0)
}
mutating func sendToBack(item: Element) {
move(item, to: endIndex-1)
}
}
extension Array {
mutating func move(at index: Index, to newIndex: Index) {
insert(remove(at: index), at: newIndex)
}
}
We can use swap method to swap items in an array :
var arr = ["one", "two", "three", "four", "five"]
// Swap elements at index: 2 and 3
print(arr)
swap(&arr[2], &arr[3])
print(arr)
#ian has provided good solution but it will be crash when array become out of bound added check for that too
extension Array where Element: Equatable {
public mutating func move(_ element: Element, to newIndex: Index) {
if let oldIndex: Int = index(of: element) {
self.move(from: oldIndex, to: newIndex)
}
}
public mutating func moveToFirst(item: Element) {
self.move(item, to: 0)
}
public mutating func move(from oldIndex: Index, to newIndex: Index) {
// won't rebuild array and will be super efficient.
if oldIndex == newIndex { return }
// Index out of bound handle here
if newIndex >= self.count { return }
// Don't work for free and use swap when indices are next to each other - this
if abs(newIndex - oldIndex) == 1 { return self.swapAt(oldIndex, newIndex) }
// Remove at old index and insert at new location
self.insert(self.remove(at: oldIndex), at: newIndex)
}
}
There is no move functionality in swift for arrays. you can take an object at an index by removing it from there and place it in your favourite index by using 'insert'
var swiftarray = [1,2,3,4]
let myobject = swiftarray.removeAtIndex(1) // 2 is the object at 1st index
let myindex = 3
swiftarray.insert(myobject, atIndex: myindex) // if you want to insert the object to a particular index here it is 3
swiftarray.append(myobject) // if you want to move the object to last index
Swift 4 - Solution for moving a group of items from an IndexSet of indices, grouping them and moving them to a destination index. Realised through an extension to RangeReplaceableCollection. Includes a method to remove and return all items in an IndexSet. I wasn't sure how to constrain the extension to a more generalised form than to constrain the element than integer while maintaining the ability to construct IndexSets as my knowledge of Swift Protocols is not that extensive.
extension RangeReplaceableCollection where Self.Indices.Element == Int {
/**
Removes the items contained in an `IndexSet` from the collection.
Items outside of the collection range will be ignored.
- Parameter indexSet: The set of indices to be removed.
- Returns: Returns the removed items as an `Array<Self.Element>`.
*/
#discardableResult
mutating func removeItems(in indexSet: IndexSet) -> [Self.Element] {
var returnItems = [Self.Element]()
for (index, _) in self.enumerated().reversed() {
if indexSet.contains(index) {
returnItems.insert(self.remove(at: index), at: startIndex)
}
}
return returnItems
}
/**
Moves a set of items with indices contained in an `IndexSet` to a
destination index within the collection.
- Parameters:
- indexSet: The `IndexSet` of items to move.
- destinationIndex: The destination index to which to move the items.
- Returns: `true` if the operation completes successfully else `false`.
If any items fall outside of the range of the collection this function
will fail with a fatal error.
*/
#discardableResult
mutating func moveItems(from indexSet: IndexSet, to destinationIndex: Index) -> Bool {
guard indexSet.isSubset(of: IndexSet(indices)) else {
debugPrint("Source indices out of range.")
return false
}
guard (0..<self.count + indexSet.count).contains(destinationIndex) else {
debugPrint("Destination index out of range.")
return false
}
let itemsToMove = self.removeItems(in: indexSet)
let modifiedDestinationIndex:Int = {
return destinationIndex - indexSet.filter { destinationIndex > $0 }.count
}()
self.insert(contentsOf: itemsToMove, at: modifiedDestinationIndex)
return true
}
}
Here's a solution with functions to both change the array in-place and to return a changed array:
extension Array {
func rearranged(from fromIndex: Int, to toIndex: Int) -> [Element] {
var arr = self
let element = arr.remove(at: fromIndex)
if toIndex >= self.count {
arr.append(element)
} else {
arr.insert(element, at: toIndex)
}
return arr
}
mutating func rearrange(from fromIndex: Int, to toIndex: Int) {
let element = self.remove(at: fromIndex)
if toIndex >= self.count {
self.append(element)
} else {
self.insert(element, at: toIndex)
}
}
}
Since macOS 10.15, iOS 14, MutableCollection has the method move(fromOffsets:toOffset:).
https://developer.apple.com/documentation/swift/mutablecollection/move(fromoffsets:tooffset:)
Update with Swift 4,
Swipe array index
for (index,addres) in self.address.enumerated() {
if addres.defaultShipping == true{
let defaultShipping = self.address.remove(at: index)
self.address.insert(defaultShipping, at: 0)
}
}
Efficient solution:
extension Array
{
mutating func move(from sourceIndex: Int, to destinationIndex: Int)
{
guard
sourceIndex != destinationIndex
&& Swift.min(sourceIndex, destinationIndex) >= 0
&& Swift.max(sourceIndex, destinationIndex) < count
else {
return
}
let direction = sourceIndex < destinationIndex ? 1 : -1
var sourceIndex = sourceIndex
repeat {
let nextSourceIndex = sourceIndex + direction
swapAt(sourceIndex, nextSourceIndex)
sourceIndex = nextSourceIndex
}
while sourceIndex != destinationIndex
}
}
func adjustIndex(_ index: Int, forRemovalAt removed: Int) -> Int {
return index <= removed ? index : index - 1
}
extension Array
{
mutating func move(from oldIndex: Index, to newIndex: Index) {
insert(remove(at: oldIndex), at: adjustIndex(newIndex, forRemovalAt: oldIndex))
}
}
Swift 5 Tested
Just to add extra toppings on cake,
I've added functionality to handle Array<Dictionary<String,Any>>
Main Source of my answer here https://stackoverflow.com/a/50205000/4131763,
here is my version,
//Array+Extension.swift,
extension Array where Element: Equatable
{
mutating func move(_ element: Element, to newIndex: Index) {
if let oldIndex: Int = self.firstIndex(of: element) { self.move(from: oldIndex, to: newIndex) }
}
}
extension Array where Element == Dictionary<String, Any> {
mutating func move(_ element:Element, to newIndex: Index) {
if let oldIndex = self.firstIndex(where: { ($0.keys.first ?? "") == (element.keys.first ?? "") }) {
self.move(from: oldIndex, to: newIndex)
}
}
}
extension Array
{
mutating func move(from oldIndex: Index, to newIndex: Index) {
// Don't work for free and use swap when indices are next to each other - this
// won't rebuild array and will be super efficient.
if oldIndex == newIndex { return }
if abs(newIndex - oldIndex) == 1 { return self.swapAt(oldIndex, newIndex) }
self.insert(self.remove(at: oldIndex), at: newIndex)
}
}
HOW TO USE,
if let oldIndex = array.firstIndex(where: { ($0["ValidationTitle"] as! String) == "MEDICALNOTICEREQUIRED" }) {
let obj = array[oldIndex]
array.move(obj, to: array.startIndex)
}
if let oldIndex = array.firstIndex(where: { ($0["ValidationTitle"] as! String) == "HIGHRISKCONFIRMATION" }) {
let obj = array[oldIndex]
let oldIndexMEDICALNOTICEREQUIRED = array.firstIndex(where: { ($0["ValidationTitle"] as! String) == "MEDICALNOTICEREQUIRED" })!
array.move(obj, to: oldIndexMEDICALNOTICEREQUIRED + 1)
}
if let oldIndex = array.firstIndex(where: { ($0["ValidationTitle"] as! String) == "UNLICENCEDCONFIRMATION" }) {
let obj = array[oldIndex]
let oldIndexHIGHRISKCONFIRMATION = array.firstIndex(where: { ($0["ValidationTitle"] as! String) == "HIGHRISKCONFIRMATION" })!
array.move(obj, to: oldIndexHIGHRISKCONFIRMATION + 1)
}
Leo Dabus's solution is great however using precondition(from != to && indices.contains(from != to && indices.contains(to), "invalid indexes"), will crash the app if the conditions are not met. I changed it to guard and an if statement - if for some reason the conditions are not met, nothing happens and the app continues. I think we should avoid making extensions that may crash the app. If you wish you could make the rearrange function return a Bool - true if successful and false if failed.
The safer solution:
extension Array {
mutating func rearrange(from: Int, to: Int) {
guard from != to else { return }
//precondition(from != to && indices.contains(from) && indices.contains(to), "invalid indexes")
if indices.contains(from) && indices.contains(to) {
insert(remove(at: from), at: to)
}
}
Function(not swift but universal.. lookup/remove/insert):
func c_move_to(var array:Array,var from:Int,var to:Int):
var val = array[from]
array.remove(from)
array.insert(to,val)
return array
How to use:
print("MOVE 0 to 3 [1,2,3,4,5]" , c_move_to([1,2,3,4,5],0,3))
print("MOVE 1 to 2 [1,2,3,4,5]" , c_move_to([1,2,3,4,5],1,2))
spits out:
MOVE 0 to 3 [1,2,3,4,5][2, 3, 4, 1, 5]
MOVE 1 to 2 [1,2,3,4,5][1, 3, 2, 4, 5]
How about this solution?
The element to be changed and the element to be changed have been changed.
// Extenstion
extension Array where Element: Equatable {
mutating func change(_ element: Element, to newIndex: Index) {
if let firstIndex = self.firstIndex(of: element) {
self.insert(element, at: 0)
self.remove(at: firstIndex + 1)
}
}
}
// Example
var testArray = ["a", "b", "c", "EE", "d"]
testArray.change("EE", to: 0)
// --> Result
// ["EE", "a", "b", "c", "d"]
Related
What is the best way to identify the item that precedes or follows a specified object in a Swift array, while protecting against out of bounds errors?
A good way to approach this is with an extension on the Swift Array, or in this case a more generalised solution for all BidirectionalCollection objects, including arrays.
The following provides methods for getting the next or previous object after your specified object from an array, with an optional parameter if you want the function to loop at the ends of the array.
These functions return nil if the original object is not present in the Array, and also if you ask for the previous item to the first object or the item that follows the last object, for the non-looping functions.
//
// Array+Iterator.swift
//
extension BidirectionalCollection where Iterator.Element: Equatable {
typealias Element = Self.Iterator.Element
func after(_ item: Element, loop: Bool = false) -> Element? {
if let itemIndex = self.index(of: item) {
let lastItem: Bool = (index(after:itemIndex) == endIndex)
if loop && lastItem {
return self.first
} else if lastItem {
return nil
} else {
return self[index(after:itemIndex)]
}
}
return nil
}
func before(_ item: Element, loop: Bool = false) -> Element? {
if let itemIndex = self.index(of: item) {
let firstItem: Bool = (itemIndex == startIndex)
if loop && firstItem {
return self.last
} else if firstItem {
return nil
} else {
return self[index(before:itemIndex)]
}
}
return nil
}
}
Usage:
If you have an array of your children, and want to know the child that comes after Jane, you would use the following:
let nextChild = children.after(jane)
If you simply want to know whose turn it is to do the dishes, and Sammy did them last night, you'd instead use:
let dishwasherTonight = children.after(sammy, loop: true)
That way, if Sammy is the youngest child, his oldest sibling will be assigned to wash the dishes tonight as we loop back to the start of the array.
Postscript: note re the comparison to endIndex in the code the definition of that property:
You can access an element of a collection through its subscript by
using any valid index except the collection’s endIndex property. This
property is a “past the end” index that does not correspond with any
element of the collection.
I propose a simpler and more thorough implementation:
extension Collection where Iterator.Element: Equatable {
typealias Element = Self.Iterator.Element
func safeIndex(after index: Index) -> Index? {
let nextIndex = self.index(after: index)
return (nextIndex < self.endIndex) ? nextIndex : nil
}
func index(afterWithWrapAround index: Index) -> Index {
return self.safeIndex(after: index) ?? self.startIndex
}
func item(after item: Element) -> Element? {
return self.firstIndex(of: item)
.flatMap(self.safeIndex(after:))
.map{ self[$0] }
}
func item(afterWithWrapAround item: Element) -> Element? {
return self.firstIndex(of: item)
.map(self.index(afterWithWrapAround:))
.map{ self[$0] }
}
}
extension BidirectionalCollection where Iterator.Element: Equatable {
typealias Element = Self.Iterator.Element
func safeIndex(before index: Index) -> Index? {
let previousIndex = self.index(before: index)
return (self.startIndex <= previousIndex) ? previousIndex : nil
}
func index(beforeWithWrapAround index: Index) -> Index {
return self.safeIndex(before: index) ?? self.index(before: self.endIndex)
}
func item(before item: Element) -> Element? {
return self.firstIndex(of: item)
.flatMap(self.safeIndex(before:))
.map{ self[$0] }
}
func item(beforeWithWrapAround item: Element) -> Element? {
return self.firstIndex(of: item)
.map(self.index(beforeWithWrapAround:))
.map{ self[$0] }
}
}
Why not use Swift's factory methods?
try this to get a previous item to an object.
let currentIndex = yourArr.index(of: "0")
let previousVal = yourArr[currentIndex-1]
First get the index of object you want previous item for,
then get the item of that index-1.
import UIKit
extension Array where Element : Equatable {
/// Finds next element of the array, returns nil if there is no next element
func nextElement(element: Element, completion: #escaping(Element?)->()){
let currentIndex = self.firstIndex { $0 == element}
if let currIndex = currentIndex?.advanced(by: 1) {
completion(currIndex >= self.endIndex ? nil : self[currIndex])
}else{
completion(nil)
}
}
/// Finds prev element of the array, returns nil if there is no previous element
func prevElement(element: Element, completion: #escaping(Element?)->()){
let currentIndex = self.firstIndex { $0 == element}
if let currIndex = currentIndex?.advanced(by: -1) {
completion(currIndex <= self.endIndex ? nil : self[currIndex])
}else{
completion(nil)
}
}
}
I'm writing a Swift extension that checks if two or more CGPoints in array have the same coordinates. Having this code I can check all points in array.
But how to check just several elements (not all)?
Here's the extension...
import Foundation
extension Array where Element : Equatable {
func equalCoordinates() -> Bool {
if let firstElement = first {
return dropFirst().contains { $0 == firstElement }
}
return true
}
}
If two (or more) red CGPoints have identical coordinates they must be turned into green ones.
...and a code in ViewController using equalCoordinates() method:
func drawn() {
let colorArray = array.map { $0.pointCoord()[0] }
for dot in array {
for cPoint in dot.pointCoord() {
if colorArray.equalCoordinates() {
let altColor = dot.alternativePointColour()
draw(cPoint, color: altColor)
} else {
let color = dot.pointColour()
draw(cPoint, color: color)
}
}
}
}
...........
Swift.print(colorArray.equalCoordinates())
...........
With absolutely no concern given to efficiency (that can be improved depending on the size of your data), this is how I'd probably go about it. Each piece is pretty simple, so you should be able to adapt it to a wide variety of different outputs (if you prefer something other than IndexSet for instance).
import Foundation
import CoreGraphics
// We could put this on Collection rather than Array, but then we'd have to rewrite
// IndexSet on generic indices or use [Index].
extension Array where Element : Equatable {
func uniqueElements() -> [Element] {
// This is O(n^2), but it's hard to beat that without adding either
// Hashable (for Set) or Comparable (to pre-sort) to the requirements,
// neither of which CGPoints have by default.
var uniqueElements: [Element] = []
for element in self {
if !uniqueElements.contains(element) {
uniqueElements.append(element)
}
}
return uniqueElements
}
func indexSet(of element: Element) -> IndexSet {
var indices = IndexSet()
for (index, member) in enumerated() {
if element == member {
indices.insert(index)
}
}
return indices
}
func indexSetsGroupedByEquality() -> [(element: Element, indexSet: IndexSet)] {
return uniqueElements().map { element in (element, indexSet(of: element)) }
}
func indexSetsOfCollidingElements() -> [IndexSet] {
func hasCollisions(_: Element, indexSet: IndexSet) -> Bool { return indexSet.count > 1 }
return indexSetsGroupedByEquality()
.filter(hasCollisions)
.map { $0.indexSet }
}
}
let points = [
CGPoint(x:1,y:1),
CGPoint(x:2,y:1),
CGPoint(x:1,y:1),
CGPoint(x:3,y:1),
CGPoint(x:2,y:1),
]
print(points.indexSetsOfCollidingElements().map(Array.init))
// [[0, 2], [1, 4]]
Swift 2.2 version
extension Array where Element : Equatable {
func uniqueElements() -> [Element] {
var uniqueElements: [Element] = []
for element in self {
if !uniqueElements.contains(element) {
uniqueElements.append(element)
}
}
return uniqueElements
}
func indexSet(of element: Element) -> NSIndexSet {
let indices = NSIndexSet()
for (index, member) in enumerate() {
if element == member {
indices.insertValue(index, inPropertyWithKey: "")
}
}
return indices
}
func indexSetsGroupedByEquality() -> [(element: Element,
indexSet: NSIndexSet)] {
return uniqueElements().map { element in
(element, indexSet(of: element))
}
}
func indexSetsOfCollidingElements() -> [NSIndexSet] {
func hasCollisions(_: Element, indexSet: NSIndexSet) -> Bool {
return indexSet.count > 0
}
return indexSetsGroupedByEquality().filter(hasCollisions)
.map { $0.indexSet }
}
}
Is there a easy way to sort an array by the count of numbers? And if a number have the same count put the highest number first.
[2,8,2,6,1,8,2,6,6]
to
[6,6,6,2,2,2,8,8,1]
What you are looking for is a way to get the frequencies of values.
As long as the values are Hashable this function will work:
It extends all sequence types where the Element is Hashable, so an array of Int will work.
extension SequenceType where Generator.Element : Hashable {
func frequencies() -> [Generator.Element:Int] {
var results : [Generator.Element:Int] = [:]
for element in self {
results[element] = (results[element] ?? 0) + 1
}
return results
}
}
Then you can do this:
let alpha = [2,8,2,6,1,8,2,6,6]
let sorted = alpha.frequencies().sort {
if $0.1 > $1.1 { // if the frequency is higher, return true
return true
} else if $0.1 == $1.1 { // if the frequency is equal
return $0.0 > $1.0 // return value is higher
} else {
return false // else return false
}
}
Even better, you can now create another extension to sequence types.
Now they need to conform to Comparable as well as Hashable
extension SequenceType where Generator.Element : protocol<Hashable,Comparable> {
func sortByFrequency() -> [Generator.Element] {
// the same sort function as before
let sorted = self.frequencies().sort {
if $0.1 > $1.1 {
return true
} else if $0.1 == $1.1 {
return $0.0 > $1.0
} else {
return false
}
}
// this is to convert back from the dictionary to an array
var sortedValues : [Generator.Element] = []
sorted.forEach { // for each time the value was found
for _ in 0..<$0.1 {
sortedValues.append($0.0) // append
}
}
return sortedValues
}
}
Your final usage of all this will look like this :
let sorted = alpha.sortByFrequency() // [6, 6, 6, 2, 2, 2, 8, 8, 1]
Super clean :)
If you prefer a function closer to sort itself you can also use this :
extension SequenceType where Generator.Element : Hashable {
func sortedFrequency(#noescape isOrderedBefore: ((Self.Generator.Element,Int), (Self.Generator.Element,Int)) -> Bool) -> [Generator.Element] {
let sorted = self.frequencies().sort {
return isOrderedBefore($0,$1) // this uses the closure to sort
}
var sortedValues : [Generator.Element] = []
sorted.forEach {
for _ in 0..<$0.1 {
sortedValues.append($0.0)
}
}
return sortedValues
}
}
The extension above converts the array to a frequency dictionary internally and just asks you to input a closure that returns a Bool. Then you can apply different sorting depending on your needs.
Because you pass the closure with the sorting logic to this function the Elements of the SequenceType no longer need to be comparable.
Cheat sheet for all the shorthand:
$0 // first element
$1 // second element
$0.0 // value of first element
$0.1 // frequency of first element
Sorting :
let sortedB = alpha.sortedFrequency {
if $0.1 > $1.1 {
return true
} else if $0.1 == $1.1 {
return $0.0 > $1.0
} else {
return false
}
} // [6, 6, 6, 2, 2, 2, 8, 8, 1]
I'm not sure if this is the most efficient way to do it, but I think it is fairly elegant:
extension Array where Element: Equatable {
func subArrays() -> [[Element]] {
if self.isEmpty {
return [[]]
} else {
let slice = self.filter { $0 == self[0] }
let rest = self.filter { $0 != self[0] }
return rest.isEmpty
? [slice]
: [slice] + rest.subArrays()
}
}
func sortByFrequency(secondarySort: ((Element, Element) -> Bool)? = nil) -> [Element] {
return self.subArrays()
.sort { secondarySort?($0[0], $1[0]) ?? false }
.sort { $0.count > $1.count }
.flatMap { $0 }
}
}
let nums = [2,8,2,6,1,8,2,6,6]
print(nums.sortByFrequency(>)) // [6, 6, 6, 2, 2, 2, 8, 8, 1]
The function subArrays just breaks the array down into an array of sub-arrays for each value in the original array - i.e., you'd get [[2,2,2],[8,8],[6,6,6],[1]] for the input that you provided.
sortByFrequency sorts the output of subArrays and then flatMaps to get the answer.
EDIT: I modified sortByFrequency to add the optional secondarySearch parameter. That allows you to control how you want items that occur at the same frequency to be sorted. Or, just accept the default nil and they won't be sorted by anything other than frequency.
Also, I modified the extension to indicate that Element only needs to conform to Equatable, not Comparable.
//: Playground - noun: a place where people can play
import UIKit
var arr1 = [2,8,2,6,1,8,2,6,6]
var arr2 = [6,6,6,2,2,2,8,8,1]
var counting = [Int: Int]()
// fill counting dictionary
for num in arr1 {
if counting[num] != nil {
counting[num]!++
} else {
counting[num] = 1
}
}
// [6: 3, 2: 3, 8: 2, 1: 1]
print(counting)
func order(i1: Int, i2: Int) -> Bool {
let count1 = counting[i1]
let count2 = counting[i2]
// if counting is the same: compare which number is greater
if count1 == count2 {
return i1 > i2
} else {
return count1 > count2
}
}
// [6, 6, 6, 2, 2, 2, 8, 8, 1]
print(arr1.sort(order))
print(arr2)
Using grouping in Dictionary:
var entries = [1,2,3,3,1,3,5,6,3,4,1,5,5,5,5]
extension Sequence where Element : Hashable {
func byFrequency() -> [Element] {
Dictionary(grouping: self, by: {$0}).sorted{ (a, b) in
a.value.count > b.value.count
}.map { $0.key}
}
}
print(entries.byFrequency().first)
Prints 5
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!)
}
}
}