Swift 3 Array extension error - ios

I want to insert 0 always to the array at 0th index, I am trying to write extension on array. But giving some error like :
Cannot invoke 'insert' with an argument list of type '(Int, at: Int)'
Here is my code
extension Array {
mutating func printWithZero() -> Array
{
self.insert(0, at: 0)
return self
}
}

Your extension, as you have defined it, applies to arrays of all types. Unfortunately, it isn't possible to insert a 0 into just any array. You can't, for instance, insert 0 into a [String].
You need to make this extension work only with arrays that can contain the integer literal 0. You can do this by adding a constraint on the Element type of the array that specifies that the type is ExpressibleByIntegerLiteral:
extension Array where Element: ExpressibleByIntegerLiteral {
mutating func printWithZero() -> Array
{
self.insert(0, at: 0)
return self
}
}
Example:
var a = [1, 2, 3]
let b = a.printWithZero()
print(a) // [0, 1, 2, 3]
print(b) // [0, 1, 2, 3]
// Works with an array of Doubles too
var c = [1.5, 2.5, 3.5]
let d = c.printWithZero()
print(c) // [0, 1.5, 2.5, 3.5]
print(d) // [0, 1.5, 2.5, 3.5]

I would use this simple code, no extension - Swift 3:
var myArray = [1,2,3]
myArray.insert(0, at: 0)
print(myArray) // [0,1,2,3]

Related

Why does Swift Array contains(_:) method accept nil argument when it must be non-optional?

I simplified my code as much as I could to make it still be a reproducible example.
When I use contains like this in a chain of calls it does compile, work and contains accepts nil when it shouldn't, I think.
let array = [1, 2, 3, 4, 5].filter { _ in
[1, 2, 3].map { smallNumber in
"\(smallNumber)"
}
.contains(nil)
}
But when I assign map result to a variable and then call contains with nil value the code doesn't even compile.
let array = [1, 2, 3, 4, 5].filter { _ in
let mappedNumbers = [1, 2, 3].map { smallNumber in
"\(smallNumber)"
}
return mappedNumbers.contains(nil)
}
Xcode is complaining about 'nil' is not compatible with expected argument type 'String', that is right.
I expect the same error in the first example.
The compiler can automatically wrap a value into an optional, if required by the context. That is what makes simple assignments like
let value: Int? = 123
possible. In your first example, the return type of the closure is inferred as String? from the context, so that the map returns [String?], and .contains(nil) can be applied to it. I.e. the compiler understands the code as
let array = [1, 2, 3, 4, 5].filter { _ in
[1, 2, 3].map { smallNumber -> String? in
"\(smallNumber)"
}
.contains(nil)
}
In the second example the compiler does not have that context, and mappedNumbers has the type [String]. You can make it compile by specifying the closure return type String? explicitly:
let array = [1, 2, 3, 4, 5].filter { _ in
let mappedNumbers = [1, 2, 3].map { smallNumber -> String? in
"\(smallNumber)"
}
return mappedNumbers.contains(nil)
}

Get Subset of array based on the occurrence of elements

I have an array like:
var arr = [4,1,5,5,3]
I want to fetch subset from the array based on the occurrence of elements in it.
For example:
Elements with frequency 1 is {4,1,3}
Elements with frequency 2 is {5,5}
I followed this StackOverflow question but unable to figure out how to do the above thing.
Is there any way I can do this?
You can use an NSCountedSet to get the count of all elements in arr, then you can build a Dictionary, where the keys will be the number of occurencies for the elements and the values will be Arrays of the elements with key number of occurences. By iterating through Set(arr) rather than simply arr to build the Dictionary, you can make sure that repeating elements are only added once to the Dictionary (so for instance with your original example, 5 wouldn't be added twice as having a frequency of 2).
For the printing, you just need to iterate through the keys of the Dictionary and print the keys along with their corresponding values. I just sorted the keys to make the printing go in ascending order of number of occurences.
let arr = [4,1,5,5,3,2,3,6,2,7,8,2,7,2,8,8,8,7]
let counts = NSCountedSet(array: arr)
var countDict = [Int:[Int]]()
for element in Set(arr) {
countDict[counts.count(for: element), default: []].append(element)
}
countDict
for freq in countDict.keys.sorted() {
print("Elements with frequency \(freq) are {\(countDict[freq]!)}")
}
Output:
Elements with frequency 1 are {[4, 6, 1]}
Elements with frequency 2 are {[5, 3]}
Elements with frequency 3 are {[7]}
Elements with frequency 4 are {[2, 8]}
Swift 3 version:
let arr = [4,1,5,5,3,2,3,6,2,7,8,2,7,2,8,8,8,7]
let counts = NSCountedSet(array: arr)
var countDict = [Int:[Int]]()
for element in Set(arr) {
if countDict[counts.count(for: element)] != nil {
countDict[counts.count(for: element)]!.append(element)
} else {
countDict[counts.count(for: element)] = [element]
}
}
for freq in countDict.keys.sorted() {
print("Elements with frequency \(freq) are {\(countDict[freq]!)}")
}
You just need to get the occurrences of the elements and filter the elements that only occurs once or more than once as shown in this answer:
extension Array where Element: Hashable {
// Swift 4 or later
var occurrences: [Element: Int] {
return reduce(into: [:]) { $0[$1, default: 0] += 1 }
}
// // for Swift 3 or earlier
// var occurrences: [Element: Int] {
// var result: [Element: Int] = [:]
// forEach{ result[$0] = (result[$0] ?? 0) + 1}
// return result
// }
func frequencies(where isIncluded: (Int) -> Bool) -> Array {
return filter{ isIncluded(occurrences[$0] ?? 0) }
}
}
Playground Testing:
let arr = [5, 4, 1, 5, 5, 3, 5, 3]
let frequency1 = arr.frequencies {$0 == 1} // [4, 1]
let frequency2 = arr.frequencies {$0 == 2} // [3, 3]
let frequency3orMore = arr.frequencies {$0 >= 3} // [5, 5, 5, 5]
This is it:
func getSubset(of array: [Int], withFrequency frequency: Int) -> [Int]
{
var counts: [Int: Int] = [:]
for item in array
{
counts[item] = (counts[item] ?? 0) + 1
}
let filtered = counts.filter{ $0.value == frequency}
return Array(filtered.keys)
}
This is pure Swift (not using good old Next Step classes) and is using ideas from the SO link you supplied.
The counts dictionary contains the frequencies (value) of each of the int-values (key) in your array: [int-value : frequency].

Swift - Determine if Array1 contains at least one object from Array2

I have 2 Arrays. Say, array1 = [1,2,3,4,5] and array2 = [2,3]. How could I check in swift if array1 contains at least one item from array2?
You can do this by simply passing in your array2's contains function into your array1's contains function (or vice versa), as your elements are Equatable.
let array1 = [2, 3, 4, 5]
let array2 = [20, 15, 2, 7]
// this is just shorthand for array1.contains(where: { array2.contains($0) })
if array1.contains(where: array2.contains) {
print("Array 1 and array 2 share at least one common element")
} else {
print("Array 1 doesn't contains any elements from array 2")
}
This works by looping through array 1's elements. For each element, it will then loop through array 2 to check if it exists in that array. If it finds that element, it will break and return true – else false.
This works because there are actually two flavours of contains. One takes a closure in order to check each element against a custom predicate, and the other just compares an element directly. In this example, array1 is using the closure version, and array2 is using the element version. And that is the reason you can pass a contains function into another contains function.
Although, as correctly pointed out by #AMomchilov, the above algorithm is O(n2). A good set intersection algorithm is O(n), as element lookup is O(1). Therefore if your code is performance critical, you should definitely use sets to do this (if your elements are Hashable), as shown by #simpleBob.
Although if you want to take advantage of the early exit that contains gives you, you'll want to do something like this:
extension Sequence where Iterator.Element : Hashable {
func intersects<S : Sequence>(with sequence: S) -> Bool
where S.Iterator.Element == Iterator.Element
{
let sequenceSet = Set(sequence)
return self.contains(where: sequenceSet.contains)
}
}
if array1.intersects(with: array2) {
print("Array 1 and array 2 share at least one common element")
} else {
print("Array 1 doesn't contains any elements from array 2")
}
This works much the same as the using the array's contains method – with the significant difference of the fact that the arraySet.contains method is now O(1). Therefore the entire method will now run at O(n) (where n is the greater length of the two sequences), with the possibility of exiting early.
With Swift 5, you can use one of the following paths in order to find if two arrays have common elements or not.
#1. Using Set isDisjoint(with:) method
Set has a method called isDisjoint(with:). isDisjoint(with:) has the following declaration:
func isDisjoint(with other: Set<Element>) -> Bool
Returns a Boolean value that indicates whether the set has no members in common with the given sequence.
In order to test if two arrays have no common elements, you can use the Playground sample code below that implements isDisjoint(with:):
let array1 = [1, 3, 6, 18, 24]
let array2 = [50, 100, 200]
let hasNoCommonElement = Set(array1).isDisjoint(with: array2)
print(hasNoCommonElement) // prints: true
#2. Using Set intersection(_:) method
Set has a method called intersection(_:). intersection(_:) has the following declaration:
func intersection<S>(_ other: S) -> Set<Element> where Element == S.Element, S : Sequence
Returns a new set with the elements that are common to both this set and the given sequence.
In order to test if two arrays have no common elements or one or more common elements, you can use the Playground sample code below that implements intersection(_:):
let array1 = [1, 3, 6, 18, 24]
let array2 = [2, 3, 18]
let intersection = Set(array1).intersection(array2)
print(intersection) // prints: [18, 3]
let hasCommonElement = !intersection.isEmpty
print(hasCommonElement) // prints: true
An alternative way would be using Sets:
let array1 = [1,2,3,4,5]
let array2 = [2,3]
let set1 = Set(array1)
let intersect = set1.intersect(array2)
if !intersect.isEmpty {
// do something with the intersecting elements
}
Swift 5
Just make an extension
public extension Sequence where Element: Equatable {
func contains(anyOf sequence: [Element]) -> Bool {
return self.filter { sequence.contains($0) }.count > 0
}
}
Use:
let someArray = ["one", "two", "three"]
let string = "onE, Cat, dog"
let intersects = string
.lowercased()
.replacingOccurrences(of: " ", with: "")
.components(separatedBy: ",")
.contains(anyOf: someArray)
print(intersects) // true
let a1 = [1, 2, 3]
let a2 = [2, 3, 4]
Option 1
a2.filter { a1.contains($0) }.count > 1
Option 2
a2.reduce(false, combine: { $0 || a1.contains($1) })
Hope this helps.
//
// Array+CommonElements.swift
//
import Foundation
public extension Array where Element: Hashable {
func set() -> Set<Array.Element> {
return Set(self)
}
func isSubset(of array: Array) -> Bool {
self.set().isSubset(of: array.set())
}
func isSuperset(of array: Array) -> Bool {
self.set().isSuperset(of: array.set())
}
func commonElements(between array: Array) -> Array {
let intersection = self.set().intersection(array.set())
return intersection.map({ $0 })
}
func hasCommonElements(with array: Array) -> Bool {
return self.commonElements(between: array).count >= 1 ? true : false
}
}

How to remove single object in array from multiple matching object

var testarray = NSArray()
testarray = [1,2,2,3,4,5,3]
print(testarray)
testarray.removeObject(2)
I want to remove single object from multiple matching object like
myArray = [1,2,2,3,4,3]
When I remove
myArray.removeObject(2)
then both objects are deleted. I want remove only single object.
I tried to use many extension but no one is working properly. I have already used this link.
Swift 2
Solution when using a simple Swift array:
var myArray = [1, 2, 2, 3, 4, 3]
if let index = myArray.indexOf(2) {
myArray.removeAtIndex(index)
}
It works because .indexOf only returns the first occurence of the found object, as an Optional (it will be nil if object not found).
It works a bit differently if you're using NSMutableArray:
let nsarr = NSMutableArray(array: [1, 2, 2, 3, 4, 3])
let index = nsarr.indexOfObject(2)
if index < Int.max {
nsarr.removeObjectAtIndex(index)
}
Here .indexOfObject will return Int.max when failing to find an object at this index, so we check for this specific error before removing the object.
Swift 3
The syntax has changed but the idea is the same.
Array:
var myArray = [1, 2, 2, 3, 4, 3]
if let index = myArray.index(of: 2) {
myArray.remove(at: index)
}
myArray // [1, 2, 3, 4, 3]
NSMutableArray:
let myArray = NSMutableArray(array: [1, 2, 2, 3, 4, 3])
let index = myArray.index(of: 2)
if index < Int.max {
myArray.removeObject(at: index)
}
myArray // [1, 2, 3, 4, 3]
In Swift 3 we call index(of:) on both Array and NSMutableArray, but they still behave differently for different collection types, like indexOf and indexOfObject did in Swift 2.
Swift 5: getting index of the first occurrence
if let i = yourArray.firstIndex(of: yourObject) {
yourArray.remove(at: i)
}
If you want to remove all duplicate objects then you can use below code.
var testarray = NSArray()
testarray = [1,2,2,3,4,5,3]
let set = NSSet(array: testarray as [AnyObject])
print(set.allObjects)

Swift Array Running Total to x item

Swift has a great function .reduce that provides a running total for an entire array:
let array = [1, 2, 3, 4, 5]
let addResult = array.reduce(0) { $0 + $1 }
// addResult is 15
let multiplyResult = array.reduce(1) { $0 * $1 }
// multiplyResult is 120
But is there a simple way to use this for a running total only up to a specific element? For example for an array of [1, 2, 3, 4, 5] is there a way to use the .reduce function to return a running total for only up to the 3rd item (1+2+3=6)?
(I know I can always do a loop, just trying to learn all the possibilities with swift)
Besides using the reduce method of arrays, as in #MartinR's answer, it's also possible to use the global reduce function along with enumerate
The enumerate function returns a sequence of tuples, whose first element (named index) is the element index and the second (named element) is the element value.
So you can achieve the same result with the following code:
let sum = reduce(enumerate(array), 0) {
$0 + ($1.index < 3 ? $1.element : 0)
}
The advantage of this method is that you can apply more complex rules to exclude elements - for example, to sum all elements having even index:
let sum = reduce(enumerate(array), 0) {
$0 + ($1.index % 2 == 0 ? $1.element : 0)
}
The closure called by reduce() has no information about the
current index.
Theoretically it is possible to create a closure that
uses a captured variable to "remember" how often it has been called:
func addFirst(var count : Int) -> (Int, Int) -> Int {
return {
count-- > 0 ? $0 + $1 : $0
}
}
let array = [1, 2, 3, 4, 5]
let sumOfFirstThreeItems = array.reduce(0, addFirst(3) )
But this would still run over the whole array and not just the
first 3 elements. There is no way to "stop" the process.
A much simpler way is to operate on an array slice:
let array = [1, 2, 3, 4, 5]
let sumOfFirstThreeItems = array[0 ..< 3].reduce(0) { $0 + $1 }
A slice is an Array-like type that represents a sub-sequence of any
Array, ContiguousArray, or other Slice.

Resources