Find an object in array?

IosSwift

Ios Problem Overview


Does Swift have something like _.findWhere in Underscore.js?

I have an array of structs of type T and would like to check if array contains a struct object whose name property is equal to Foo.

Tried to use find() and filter() but they only work with primitive types, e.g. String or Int. Throws an error about not conforming to Equitable protocol or something like that.

Ios Solutions


Solution 1 - Ios

SWIFT 5

Check if the element exists

if array.contains(where: {$0.name == "foo"}) {
   // it exists, do something
} else {
   //item could not be found
}

Get the element

if let foo = array.first(where: {$0.name == "foo"}) {
   // do something with foo
} else {
   // item could not be found
}

Get the element and its offset

if let foo = array.enumerated().first(where: {$0.element.name == "foo"}) {
   // do something with foo.offset and foo.element
} else {
   // item could not be found
}

Get the offset

if let fooOffset = array.firstIndex(where: {$0.name == "foo"}) {
    // do something with fooOffset
} else {
    // item could not be found
}

Solution 2 - Ios

You can use the index method available on Array with a predicate (see Apple's documentation here).

func index(where predicate: (Element) throws -> Bool) rethrows -> Int?

For your specific example this would be:

Swift 5.0

if let i = array.firstIndex(where: { $0.name == "Foo" }) {
    return array[i]
}

Swift 3.0

if let i = array.index(where: { $0.name == Foo }) {
    return array[i]
}

Swift 2.0

if let i = array.indexOf({ $0.name == Foo }) {
    return array[i]
}

Solution 3 - Ios

FWIW, if you don't want to use custom function or extension, you can:

let array = [ .... ]
if let found = find(array.map({ $0.name }), "Foo") {
    let obj = array[found]
}

This generates name array first, then find from it.

If you have huge array, you might want to do:

if let found = find(lazy(array).map({ $0.name }), "Foo") {
    let obj = array[found]
}

or maybe:

if let found = find(lazy(array).map({ $0.name == "Foo" }), true) {
    let obj = array[found]
}

Solution 4 - Ios

Swift 3

If you need the object use:

array.first{$0.name == "Foo"}

(If you have more than one object named "Foo" then first will return the first object from an unspecified ordering)

Solution 5 - Ios

You can filter the array and then just pick the first element, as shown in https://stackoverflow.com/questions/26073331/find-object-with-property-in-array.

Or you define a custom extension

extension Array {
    
    // Returns the first element satisfying the predicate, or `nil`
    // if there is no matching element.
    func findFirstMatching<L : BooleanType>(predicate: T -> L) -> T? {
        for item in self {
            if predicate(item) {
                return item // found
            }
        }
        return nil // not found
    }
}

Usage example:

struct T {
    var name : String
}

let array = [T(name: "bar"), T(name: "baz"), T(name: "foo")]

if let item = array.findFirstMatching( { $0.name == "foo" } ) {
    // item is the first matching array element
} else {
    // not found
}

In Swift 3 you can use the existing first(where:) method (as mentioned in a comment):

if let item = array.first(where: { $0.name == "foo" }) {
    // item is the first matching array element
} else {
    // not found
}

Solution 6 - Ios

Swift 3.0

if let index = array.index(where: { $0.name == "Foo" }) {
    return array[index]
}

Swift 2.1

Filtering in object properties is now supported in swift 2.1. You can filter your array based on any value of the struct or class here is an example

for myObj in myObjList where myObj.name == "foo" {
 //object with name is foo
}

OR

for myObj in myObjList where myObj.Id > 10 {
 //objects with Id is greater than 10
}

Solution 7 - Ios

Swift 4,

Another way to achieve this using filter function,

if let object = elements.filter({ $0.title == "title" }).first {
    print("found")
} else {
    print("not found")
}

Solution 8 - Ios

Swift 3

you can use index(where:) in Swift 3

func index(where predicate: @noescape Element throws -> Bool) rethrows -> Int?

example

if let i = theArray.index(where: {$0.name == "Foo"}) {
    return theArray[i]
}

Solution 9 - Ios

Swift 2 or later

You can combine indexOf and map to write a "find element" function in a single line.

let array = [T(name: "foo"), T(name: "Foo"), T(name: "FOO")]
let foundValue = array.indexOf { $0.name == "Foo" }.map { array[$0] }
print(foundValue) // Prints "T(name: "Foo")"

Using filter + first looks cleaner, but filter evaluates all the elements in the array. indexOf + map looks complicated, but the evaluation stops when the first match in the array is found. Both the approaches have pros and cons.

Solution 10 - Ios

Another way to get access to array.index(of: Any) is by declaring your object

import Foundation
class Model: NSObject {  }

Solution 11 - Ios

Swift 3

if yourArray.contains(item) {
   //item found, do what you want
}
else{
   //item not found 
   yourArray.append(item)
}

Solution 12 - Ios

Use contains:

var yourItem:YourType!
if contains(yourArray, item){
    yourItem = item
}

Or you could try what Martin pointed you at, in the comments and give filter another try: https://stackoverflow.com/questions/26073331/find-object-with-property-in-array.

Solution 13 - Ios

Swift 3:

You can use Swifts built in functionality to find custom objects in an Array.

First you must make sure your custom object conforms to the: Equatable protocol.

class Person : Equatable { //<--- Add Equatable protocol
    let name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    //Add Equatable functionality:
    static func == (lhs: Person, rhs: Person) -> Bool {
        return (lhs.name == rhs.name)
    }
}

With Equatable functionality added to your object , Swift will now show you additional properties you can use on an array:

//create new array and populate with objects:
let p1 = Person(name: "Paul", age: 20)
let p2 = Person(name: "Mike", age: 22)
let p3 = Person(name: "Jane", age: 33)
var people = [Person]([p1,p2,p3])

//find index by object:
let index = people.index(of: p2)! //finds Index of Mike

//remove item by index:
people.remove(at: index) //removes Mike from array

Solution 14 - Ios

For Swift 3,

let index = array.index(where: {$0.name == "foo"})

Solution 15 - Ios

Use Dollar which is Lo-Dash or Underscore.js for Swift:

import Dollar

let found = $.find(array) { $0.name == "Foo" }

Solution 16 - Ios

For example, if we had an array of numbers:

let numbers = [2, 4, 6, 8, 9, 10]

We could find the first odd number like this:

let firstOdd = numbers.index { $0 % 2 == 1 }

That will send back 4 as an optional integer, because the first odd number (9) is at index four.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSahat YalkabovView Question on Stackoverflow
Solution 1 - IosDanielView Answer on Stackoverflow
Solution 2 - Iosuser3799504View Answer on Stackoverflow
Solution 3 - IosrintaroView Answer on Stackoverflow
Solution 4 - IosBrettView Answer on Stackoverflow
Solution 5 - IosMartin RView Answer on Stackoverflow
Solution 6 - IosMuhammad SaifullahView Answer on Stackoverflow
Solution 7 - IosPramod MoreView Answer on Stackoverflow
Solution 8 - IosDeokhyun KoView Answer on Stackoverflow
Solution 9 - IosYoichi TagayaView Answer on Stackoverflow
Solution 10 - Iosgabi dorofteiView Answer on Stackoverflow
Solution 11 - IosyonlauView Answer on Stackoverflow
Solution 12 - IosChristianView Answer on Stackoverflow
Solution 13 - IosGeorge FilippakosView Answer on Stackoverflow
Solution 14 - IosAmal T SView Answer on Stackoverflow
Solution 15 - IosTyler LiuView Answer on Stackoverflow
Solution 16 - IosAmul4608View Answer on Stackoverflow