Array transform having failable initialiser - swift

I am using Swift 1.2 in Xcode 6.3.1
Following is my Person struct
struct Person {
let age: Int
init?(age: Int) { //Failable init
if age > 100 { return nil }
self.age = age
}
}
I am having a list of ages against which I have to make Person Objects.
I have made playground file.
let arr = Array(1...150) //Sample set of ages against which Person is created
var personList: [Person]!
and
personList = arr.map({ (val: Int) -> Person? in
return Person(age: val) //Makes object of type Person?
}).filter {
$0 != nil
}.map {
return $0!
}
Here I have uses map - filter - map because the first map invokes failable intializer, (hence it returns Person?) and personList is of type [Person].
Hence second function filters all the non nil objects and third map forcefully opens to optional therby making Person? to Person.
Is there a more easy/readable way out ? Chaining map-filter-map definitely seems to be an overkill for this

You can use flatMap to get rid of any nils in the array, this tutorial discusses the method in length, but the following will work best:
let personList = arr.flatMap { Person(age: $0) }

Note: This answer was given for Swift 1.2, the current
version at the time the question was posted. Since Swift 2 there is a better solution, see #Jeremie's answer.
I don't know of a built-in function that combines filter()
and map(). You can write the code slightly more compact using
the shorthand argument $0 in all closures:
let personList = arr.map { Person(age: $0) }
.filter { $0 != nil }
.map { $0! }
Of course you can define your own extension method which maps the
array elements and keeps only the non-nil results:
extension Array {
func optmap<U>(transform: T -> U?) -> [U] {
var result : [U] = []
for elem in self {
if let mapped = transform(elem) {
result.append(mapped)
}
}
return result
}
}
and then use it as
let personList = arr.optmap { Person(age: $0) }

You can use compactMap which is better that flatMap in this case to remove any nils in the array:
let personList = arr.compactMap { Person(age: $0) }
The Swift document declared:
Returns an array containing the non-nil results of calling the given
transformation with each element of this sequence.

Related

Forwarding function generic parameter to generic class type

I have created enum with associated value and I want to be able to dynamically update associated value. As far as I know Swift doesn't support that at the moment.
Because of that I used following approach:
enum PersonInfo {
class EnumValue<T> {
var value: T
init(_ value: T) {
self.value = value
}
}
// Instead of using String or Bool or any other type directly, use EnumValue wrapper
case firstName(EnumValue<String>)
case lastName(EnumValue<String>)
case isAdult(EnumValue<Bool>)
}
I want to add function that would update EnumValue.value property in following way:
func updateAssociatedValue<V>(_ updateValue: V) {
let mirror = Mirror(reflecting: self)
for associatedValue in mirror.children {
guard let value = associatedValue.value as? EnumValue<V> else {
continue
}
value.value = updateValue
}
}
Problem is that this guard statement always fails (guard let value = associatedValue.value as? EnumValue<V>) and I can't figure it out why.
On the other hand, when I write updateAssociatedValue with typed type then things work properly:
// This works
func updateAssociatedValue(_ updateValue: String) {
let mirror = Mirror(reflecting: self)
for associatedValue in mirror.children {
guard let value = associatedValue.value as? EnumValue<String> else {
continue
}
value.value = updateValue
}
}
Things compile normally but during the runtime guard statement always fails. Am I using generic value in some incorrect way? Should I use somehow updateValue.Type or updateValue.self (I tried but it didn't work).
Example of usage:
var array: [PersonInfo] = [
.firstName(PersonInfo.EnumValue("John")),
.lastName(PersonInfo.EnumValue("Doe")),
.isAdult(PersonInfo.EnumValue(false))
]
print(array)
// John, Doe, false
array.first?.updateAssociatedValue("Mike")
print(array)
// Mike, Doe, false
I can always reassign enum value in array but if possible I want to avoid that. That's the reason for asking this question.

swift generics return first and last element

I'm trying to get used to generics (never used them in objc) and want to write a toy function that takes an object of any type () and returns the first and last element. Hypothetically, I'd only use this on an array or a string - I keep getting an error that has no subscript members. I totally understand that the error message is telling me swift has no clue that T may potentially hold a type that does have subscripts - I just want to know how to get around this.
func firstAndLastFromCollection<T>(a:T?) {
var count: Int = 0
for item in a as! [AnyObject] {
count++
}
if count>1 {
var first = a?[0]
var last = a?[count-1]
return (first, last)
}
return something else here
}
Do I need to typecast somewhere here (which would kind of defeat the purpose here, as I'd need to downcast as either a string or an array, adding code and lessening how generic this func is)?
If you want to return the first and the last element then it's probably safe assuming the input param is an array of some kind of type.
So you can implement your function this way
func firstAndLast<T>(list:[T]) -> (first:T, last:T)? {
guard let first = list.first, last = list.last else { return nil }
return (first, last)
}
The function does return a tuple of 2 element, both have the same type of the generic element of the input array.
The returned tuple is an option because if the array is empty then nil is returned.
Examples
let nums = firstAndLast([1,2,3,4])
let words = firstAndLast(["One", "Two", "Three"])
As you can verify the type of the generic element into the array becomes the type of the elements inside the tuple.
In the example above nums is inferred to be (Int, Int)? and words (Words, Words)?
More examples
let emptyList: [String] = []
firstAndLast(emptyList) // nil
Extension
Finally you can also write this code as an extension of Array.
extension Array {
var firstAndLast: (first:Element, last:Element)? {
guard let first = self.first, last = self.last else { return nil }
return (first, last)
}
}
Now you can write
let aCoupleOfShows = ["Breaking Bad", "Better Call Saul", "Mr Robot"].firstAndLast
Again, if you check the type of the constant aCoupleOfShows you'll see that is a (first: String, last: String)?. Swift automatically did infer the correct type.
Last example
In the comments you said you wanted the first and last chars of a String. here it is the code if you use the extension above
if let chars = Array("Hello world".characters).firstAndLast {
print("First char is \(chars.first), last char is \(chars.last) ")
}
//>> First char is H, last char is d
If we are talking about collections, let's use the CollectionType:
func firstAndLastFromCollection<T: CollectionType>(a: T) -> (T.Generator.Element, T.Generator.Element)? {
guard !a.isEmpty else {
return nil
}
return (a.first!, a.lazy.reverse().first!)
}
print(firstAndLastFromCollection(["a", "b", "c"])) // ("a", "c")
print(firstAndLastFromCollection("abc".characters)) // ("a", "c")
print(firstAndLastFromCollection(0..<200)) // (0, 199)
print(firstAndLastFromCollection([] as [String])) // nil
If you specify your generic type to also conform to bidirectional index:
func firstAndLastFromCollection<T: CollectionType where T.Index : BidirectionalIndexType>(...) -> ...
then you can call last directly:
return (a.first!, a.last!)
If we decide to implement it using a category, we don't need generics at all:
extension CollectionType {
func firstAndLast() -> (Generator.Element, Generator.Element)? {
guard !self.isEmpty else {
return nil
}
return (self.first!, self.lazy.reverse().first!)
}
}
extension CollectionType where Index: BidirectionalIndexType {
func firstAndLast() -> (Generator.Element, Generator.Element)? {
guard !self.isEmpty else {
return nil
}
return (self.first!, self.last!)
}
}
print("abc".characters.firstAndLast())
Swift is a protocol oriented language. Usually you will find yourself extend protocols more than extending classes or structs.

Check if object is contained in Array [duplicate]

In Swift, how can I check if an element exists in an array? Xcode does not have any suggestions for contain, include, or has, and a quick search through the book turned up nothing. Any idea how to check for this? I know that there is a method find that returns the index number, but is there a method that returns a boolean like ruby's #include??
Example of what I need:
var elements = [1,2,3,4,5]
if elements.contains(5) {
//do something
}
Swift 2, 3, 4, 5:
let elements = [1, 2, 3, 4, 5]
if elements.contains(5) {
print("yes")
}
contains() is a protocol extension method of SequenceType (for sequences of Equatable elements) and not a global method as in
earlier releases.
Remarks:
This contains() method requires that the sequence elements
adopt the Equatable protocol, compare e.g. Andrews's answer.
If the sequence elements are instances of a NSObject subclass
then you have to override isEqual:, see NSObject subclass in Swift: hash vs hashValue, isEqual vs ==.
There is another – more general – contains() method which does not require the elements to be equatable and takes a predicate as an
argument, see e.g. Shorthand to test if an object exists in an array for Swift?.
Swift older versions:
let elements = [1,2,3,4,5]
if contains(elements, 5) {
println("yes")
}
For those who came here looking for a find and remove an object from an array:
Swift 1
if let index = find(itemList, item) {
itemList.removeAtIndex(index)
}
Swift 2
if let index = itemList.indexOf(item) {
itemList.removeAtIndex(index)
}
Swift 3, 4
if let index = itemList.index(of: item) {
itemList.remove(at: index)
}
Swift 5.2
if let index = itemList.firstIndex(of: item) {
itemList.remove(at: index)
}
Updated for Swift 2+
Note that as of Swift 3 (or even 2), the extension below is no longer necessary as the global contains function has been made into a pair of extension method on Array, which allow you to do either of:
let a = [ 1, 2, 3, 4 ]
a.contains(2) // => true, only usable if Element : Equatable
a.contains { $0 < 1 } // => false
Historical Answer for Swift 1:
Use this extension: (updated to Swift 5.2)
extension Array {
func contains<T>(obj: T) -> Bool where T: Equatable {
return !self.filter({$0 as? T == obj}).isEmpty
}
}
Use as:
array.contains(1)
If you are checking if an instance of a custom class or struct is contained in an array, you'll need to implement the Equatable protocol before you can use .contains(myObject).
For example:
struct Cup: Equatable {
let filled:Bool
}
static func ==(lhs:Cup, rhs:Cup) -> Bool { // Implement Equatable
return lhs.filled == rhs.filled
}
then you can do:
cupArray.contains(myCup)
Tip: The == override should be at the global level, not within your class/struct
I used filter.
let results = elements.filter { el in el == 5 }
if results.count > 0 {
// any matching items are in results
} else {
// not found
}
If you want, you can compress that to
if elements.filter({ el in el == 5 }).count > 0 {
}
Hope that helps.
Update for Swift 2
Hurray for default implementations!
if elements.contains(5) {
// any matching items are in results
} else {
// not found
}
(Swift 3)
Check if an element exists in an array (fulfilling some criteria), and if so, proceed working with the first such element
If the intent is:
To check whether an element exist in an array (/fulfils some boolean criteria, not necessarily equality testing),
And if so, proceed and work with the first such element,
Then an alternative to contains(_:) as blueprinted Sequence is to first(where:) of Sequence:
let elements = [1, 2, 3, 4, 5]
if let firstSuchElement = elements.first(where: { $0 == 4 }) {
print(firstSuchElement) // 4
// ...
}
In this contrived example, its usage might seem silly, but it's very useful if querying arrays of non-fundamental element types for existence of any elements fulfilling some condition. E.g.
struct Person {
let age: Int
let name: String
init(_ age: Int, _ name: String) {
self.age = age
self.name = name
}
}
let persons = [Person(17, "Fred"), Person(16, "Susan"),
Person(19, "Hannah"), Person(18, "Sarah"),
Person(23, "Sam"), Person(18, "Jane")]
if let eligableDriver = persons.first(where: { $0.age >= 18 }) {
print("\(eligableDriver.name) can possibly drive the rental car in Sweden.")
// ...
} // Hannah can possibly drive the rental car in Sweden.
let daniel = Person(18, "Daniel")
if let sameAgeAsDaniel = persons.first(where: { $0.age == daniel.age }) {
print("\(sameAgeAsDaniel.name) is the same age as \(daniel.name).")
// ...
} // Sarah is the same age as Daniel.
Any chained operations using .filter { ... some condition }.first can favourably be replaced with first(where:). The latter shows intent better, and have performance advantages over possible non-lazy appliances of .filter, as these will pass the full array prior to extracting the (possible) first element passing the filter.
Check if an element exists in an array (fulfilling some criteria), and if so, remove the first such element
A comment below queries:
How can I remove the firstSuchElement from the array?
A similar use case to the one above is to remove the first element that fulfils a given predicate. To do so, the index(where:) method of Collection (which is readily available to array collection) may be used to find the index of the first element fulfilling the predicate, whereafter the index can be used with the remove(at:) method of Array to (possible; given that it exists) remove that element.
var elements = ["a", "b", "c", "d", "e", "a", "b", "c"]
if let indexOfFirstSuchElement = elements.index(where: { $0 == "c" }) {
elements.remove(at: indexOfFirstSuchElement)
print(elements) // ["a", "b", "d", "e", "a", "b", "c"]
}
Or, if you'd like to remove the element from the array and work with, apply Optional:s map(_:) method to conditionally (for .some(...) return from index(where:)) use the result from index(where:) to remove and capture the removed element from the array (within an optional binding clause).
var elements = ["a", "b", "c", "d", "e", "a", "b", "c"]
if let firstSuchElement = elements.index(where: { $0 == "c" })
.map({ elements.remove(at: $0) }) {
// if we enter here, the first such element have now been
// remove from the array
print(elements) // ["a", "b", "d", "e", "a", "b", "c"]
// and we may work with it
print(firstSuchElement) // c
}
Note that in the contrived example above the array members are simple value types (String instances), so using a predicate to find a given member is somewhat over-kill, as we might simply test for equality using the simpler index(of:) method as shown in #DogCoffee's answer. If applying the find-and-remove approach above to the Person example, however, using index(where:) with a predicate is appropriate (since we no longer test for equality but for fulfilling a supplied predicate).
An array that contains a property that equals to
yourArray.contains(where: {$0.propertyToCheck == value })
Returns boolean.
The simplest way to accomplish this is to use filter on the array.
let result = elements.filter { $0==5 }
result will have the found element if it exists and will be empty if the element does not exist. So simply checking if result is empty will tell you whether the element exists in the array. I would use the following:
if result.isEmpty {
// element does not exist in array
} else {
// element exists
}
Swift 4/5
Another way to achieve this is with the filter function
var elements = [1,2,3,4,5]
if let object = elements.filter({ $0 == 5 }).first {
print("found")
} else {
print("not found")
}
As of Swift 2.1 NSArrays have containsObjectthat can be used like so:
if myArray.containsObject(objectImCheckingFor){
//myArray has the objectImCheckingFor
}
Array
let elements = [1, 2, 3, 4, 5, 5]
Check elements presence
elements.contains(5) // true
Get elements index
elements.firstIndex(of: 5) // 4
elements.firstIndex(of: 10) // nil
Get element count
let results = elements.filter { element in element == 5 }
results.count // 2
Just in case anybody is trying to find if an indexPath is among the selected ones (like in a UICollectionView or UITableView cellForItemAtIndexPath functions):
var isSelectedItem = false
if let selectedIndexPaths = collectionView.indexPathsForSelectedItems() as? [NSIndexPath]{
if contains(selectedIndexPaths, indexPath) {
isSelectedItem = true
}
}
if user find particular array elements then use below code same as integer value.
var arrelemnts = ["sachin", "test", "test1", "test3"]
if arrelemnts.contains("test"){
print("found") }else{
print("not found") }
Here is my little extension I just wrote to check if my delegate array contains a delegate object or not (Swift 2). :) It Also works with value types like a charm.
extension Array
{
func containsObject(object: Any) -> Bool
{
if let anObject: AnyObject = object as? AnyObject
{
for obj in self
{
if let anObj: AnyObject = obj as? AnyObject
{
if anObj === anObject { return true }
}
}
}
return false
}
}
If you have an idea how to optimize this code, than just let me know.
Swift
If you are not using object then you can user this code for contains.
let elements = [ 10, 20, 30, 40, 50]
if elements.contains(50) {
print("true")
}
If you are using NSObject Class in swift. This variables is according to my requirement. you can modify for your requirement.
var cliectScreenList = [ATModelLeadInfo]()
var cliectScreenSelectedObject: ATModelLeadInfo!
This is for a same data type.
{ $0.user_id == cliectScreenSelectedObject.user_id }
If you want to AnyObject type.
{ "\($0.user_id)" == "\(cliectScreenSelectedObject.user_id)" }
Full condition
if cliectScreenSelected.contains( { $0.user_id == cliectScreenSelectedObject.user_id } ) == false {
cliectScreenSelected.append(cliectScreenSelectedObject)
print("Object Added")
} else {
print("Object already exists")
}
what about using a hash table for the job, like this?
first, creating a "hash map" generic function, extending the Sequence protocol.
extension Sequence where Element: Hashable {
func hashMap() -> [Element: Int] {
var dict: [Element: Int] = [:]
for (i, value) in self.enumerated() {
dict[value] = i
}
return dict
}
}
This extension will work as long as the items in the array conform to Hashable, like integers or strings, here is the usage...
let numbers = Array(0...50)
let hashMappedNumbers = numbers.hashMap()
let numToDetect = 35
let indexOfnumToDetect = hashMappedNumbers[numToDetect] // returns the index of the item and if all the elements in the array are different, it will work to get the index of the object!
print(indexOfnumToDetect) // prints 35
But for now, let's just focus in check if the element is in the array.
let numExists = indexOfnumToDetect != nil // if the key does not exist
means the number is not contained in the collection.
print(numExists) // prints true
Swift 4.2 +
You can easily verify your instance is an array or not by the following function.
func verifyIsObjectOfAnArray<T>(_ object: T) -> Bool {
if let _ = object as? [T] {
return true
}
return false
}
Even you can access it as follows. You will receive nil if the object wouldn't be an array.
func verifyIsObjectOfAnArray<T>(_ object: T) -> [T]? {
if let array = object as? [T] {
return array
}
return nil
}
You can add an extension for Array as such:
extension Array {
func contains<T>(_ object: T) -> Bool where T: Equatable {
!self.filter {$0 as? T == object }.isEmpty
}
}
This can be used as:
if myArray.contains(myItem) {
// code here
}

Swift filter array of objects

class book{
var nameOfBook: String!
}
var englishBooks=[book(),book(),book()]
var arr = englishBooks.filter {
contains($0.nameOfBook, "rt")
}
I'm using this filter but with error cannot invoke filter with an argument
contains() checks if a sequence contains a given element, e.g.
if a String contains a given Character.
If your intention is to find all books where the name contains the substring "rt", then you can use rangeOfString():
var arr = englishBooks.filter {
$0.nameOfBook.rangeOfString("rt") != nil
}
or for case-insensitive comparison:
var arr = englishBooks.filter {
$0.nameOfBook.rangeOfString("rt", options: .CaseInsensitiveSearch) != nil
}
As of Swift 2, you can use
nameOfBook.containsString("rt") // or
nameOfBook.localizedCaseInsensitiveContainsString("rt")
and in Swift 3 this is
nameOfBook.contains("rt") // or
nameOfBook.localizedStandardContains("rt") // or
nameOfBook.range(of: "rt", options: .caseInsensitive) != nil
Sorry this is an old thread. Change you code slightly to properly init your variable 'nameOfBook'.
class book{
var nameOfBook: String!
init(name: String) {
nameOfBook = name
}
}
Then we can create an array of books.
var englishBooks = [book(name: "Big Nose"), book(name: "English Future
Prime Minister"), book(name: "Phenomenon")]
The array's 'filter' function takes one argument and some logics, 'contains' function can take a simplest form of a string you are searching for.
let list1 = englishBooks.filter { (name) -> Bool in
name.contains("English")
}
You can then print out list1 like so:
let list2 = arr1.map({ (book) -> String in
return book.nameOfBook
})
print(list2)
// print ["English Future Prime Minister"]
Above two snippets can be written short hand like so:
let list3 = englishBooks.filter{ ($0.nameOfBook.contains("English")) }
print(list3.map({"\($0.nameOfBook!)"}))
SWIFT 4.0
In order to filter objects and get resultant array you can use this
self.resultArray = self.upcomingAuctions.filter {
$0.auctionStatus == "waiting"
}
in case you want to delete an interval of object which has specific IDs (matchIDsToDelete) from an array of object (matches)
var matches = [Match]
var matchIDsToDelete = [String]
matches = matches.filter { !matchIDsToDelete.contains($0.matchID) }
2020 | SWIFT 5.1:
short answer:
books.filter { $0.alias.range(of: filterStr, options: .caseInsensitive) != nil }
long sample:
public filterStr = ""
public var books: [Book] = []
public var booksFiltered: [Book] {
get {
(filterStr.isEmpty )
? books
: books.filter { $0.alias.range(of: filterStr, options: .caseInsensitive) != nil }
}
}
I think this is more useful for lack of wrong typing situation.
englishBooks.filter( { $0.nameOfBook.range(of: searchText, options: .caseInsensitive) != nil}
In Swift 4.2 use the remove(where:) functionality. filter isn't doing well with memory, remove(where:) does the job better.
To do what you want:
englishBooks.removeAll { !$0.nameOfBook.contains("English") }

How can I find the index of an item in Swift? [duplicate]

This question already has answers here:
How to find index of list item in Swift?
(23 answers)
Closed 7 years ago.
Is there a method called indexof or something similar?
var array = ["Jason", "Charles", "David"]
indexOf(array, "Jason") // Should return 0
EDIT: As of Swift 3.0, you should use the .index(where:) method instead and follow the change in the Swift 2.0 edit below.
EDIT: As of Swift 2.0, you should use the indexOf method instead. It too returns nil or the first index of its argument.
if let i = array.indexOf("Jason") {
print("Jason is at index \(i)")
} else {
print("Jason isn't in the array")
}
Use the find function. It returns either nil (if the value isn't found) or the first index of the value in the array.
if let i = find(array, "Jason") {
println("Jason is at index \(i)")
} else {
println("Jason isn't in the array")
}
In Swift 2.0 (Xcode 7.1b), you can use
if let result = array.indexOf("Jason")
while find(array, "Jason") is deprecated.
I made this function like above, but it return array of indexes
extension Array {
func indexesOf<T : Equatable>(object:T) -> [Int] {
var result: [Int] = []
for (index,obj) in enumerate(self) {
if obj as T == object {
result.append(index)
}
}
return result
}
}
Maybe it will be useful for you
Array can be bridged to an NSArray, so you can use:
array.bridgeToObjectiveC().indexOfObject("Jason")
An extension of Array can work wonders here. Here's an implementation shared in this StackOverflow answer:
extension Array {
func find (includedElement: T -> Bool) -> Int? {
for (idx, element) in enumerate(self) {
if includedElement(element) {
return idx
}
}
return nil
}
}
You can add an Array Extension that does exactly what you want, i.e:
extension Array {
func indexOf<T : Equatable>(x:T) -> Int? {
for i in 0..self.count {
if self[i] as T == x {
return i
}
}
return nil
}
}
Which now lets you use .indexOf() on all Swift arrays, e.g:
["Jason", "Charles", "David"].indexOf("Jason") //0
While the response from Max is great, it does not let you find multiple indexes of multiple objects, ie. indexes of the subset of the array. If you need that functionality, extensions are, as always, your best friend
func indexesOfSubset<T : Equatable>(objects : [T]) -> [Int] {
// Create storage for filtered objectsand results
var unusedObjects = objects
var result : [Int] = []
// Enumerate through all objects in array
for (index, obj) in enumerate(self) {
// Enumerate again through all objects that has not been found
for x in unusedObjects {
// If we hit match, append result, remove it from usused objects
if obj as! T == x {
result.append(index)
unusedObjects = unusedObjects.filter( { $0 != x } )
break
}
}
}
// Get results
return result
}
Note* : Works on Swift 1.2, if you want compatibility with 1.1, replace as! -> as