user1960279
user1960279

Reputation: 514

how to filter an array on the basis of name array?

I have 2 arrays. Inside the first array I have a namelist that I want to remove from second array.

First array is simply array of strings:

var arrayNames = ["apple", "apricot", "cucumber"]

Second array is an array of custom structs:

struct fruitsData {
var name: String?
}

var secondArray = [fruitsData(name: "apple"),fruitsData(name: "apricot"), fruitsData(name: "mango"), fruitsData(name: "grapes"), fruitsData(name: "tomato"), fruitsData(name: "lichi"), fruitsData(name: "cucumber"), fruitsData(name: "brinjal")]

So how could I get the array which contains this data only:

var finalData = [fruitsData(name: "mango"), fruitsData(name: "grapes"), fruitsData(name: "tomato"), fruitsData(name: "lichi"), fruitsData(name: "brinjal")]

which does not include any name from arrayNames?

Upvotes: 1

Views: 1054

Answers (5)

dfrib
dfrib

Reputation: 73176

The natural approach when wanting to construct a subset array from a given array and some predicate is using filter. Since name of the FruitsData struct is an Optional, you'll need to (attempt to) unwrap it prior to comparing it to the non-Optional names in the arrayNames list, e.g. using the map(_:) method of Optional along with optional chaining for handling nil-valued name cases.

E.g.:

// example setup
struct FruitsData {
    var name: String?
}

let arrayNames = ["apple", "apricot", "cucumber"]

let secondArray = [
    FruitsData(name: "apple"),    FruitsData(name: "apricot"),
    FruitsData(name: "mango"),    FruitsData(name: "grapes"),
    FruitsData(name: "tomato"),   FruitsData(name: "lichi"),
    FruitsData(name: "cucumber"), FruitsData(name: "brinjal")
]

// filter 'secondArray' based on the non-existance of an
// associated fruit name in the 'arrayNames' list
let finalData = secondArray.filter {
    $0.name.map { fruitName in !arrayNames.contains(fruitName) } ?? true
}

Since String conforms to Hashable, you might also want to consider letting the list of fruit names to be excluded (arrayNames) be a Set rather than an Array, as the former will allow O(1) lookup when applying contains { ... } to it. E.g.:

let removeNames = Set(arrayNames)
let finalData = secondArray.filter {
    $0.name.map { fruitName in !removeNames.contains(fruitName) } ?? true
}                                        /* ^^^^^^^^- O(1) lookup */

If you'd also like to filter out FruitsData instances in secondArray that have nil valued name properties, you can simply with the ... ?? true predicate part above to ... ?? false: the filter operation will then filter out all FruitsData instances whose name property is either nil or present in arrayNames.

// ... filter out also FruitsData instances with 'nil' valued 'name'
let removeNames = Set(arrayNames)
let finalData = secondArray.filter {
    $0.name.map { fruitName in !removeNames.contains(fruitName) } ?? false
}

Upvotes: 0

user3441734
user3441734

Reputation: 17534

If the name is NOT expected to be an empty string, you can use

let arrayNames = ["apple"]

struct fruitsData {
    var name: String?
}
var secondArray = [fruitsData(name: "apple"),fruitsData(name: "apricot"), fruitsData()]

// [__lldb_expr_98.fruitsData(name: Optional("apple"))]
let flt1 = secondArray.filter { arrayNames.contains($0.name ?? "") }

// [__lldb_expr_87.fruitsData(name: Optional("apricot")), __lldb_expr_87.fruitsData(name: nil)]
let flt2 = secondArray.filter { !arrayNames.contains($0.name ?? "") }

Upvotes: 0

Sudipto Roy
Sudipto Roy

Reputation: 6795

Your finalData from secondArray excluding arrayNames can be achieved here . .

var finalData = secondArray.filter { !arrayNames.contains($0.name!)}

Upvotes: 1

Mr. Xcoder
Mr. Xcoder

Reputation: 4795

There are a couple of ways to do this:

  • the best way is using a filter method:

    var arrayNames = ["apple", "apricot", "cucumber"]
    var secondArray = [fruitsData(name: "apple"),fruitsData(name: "apricot"), fruitsData(name: "mango"), fruitsData(name: "grapes"), fruitsData(name: "tomato"), fruitsData(name: "lichi"), fruitsData(name: "cucumber"), fruitsData(name: "brinjal")]
    
    secondArray = secondArray.filter({$0.name != nil && !arrayNames.contains($0.name!)})
    
  • alternatively, if you want to sacrifice efficiency for the sake of readability, you can use a for-in loop alongside a helper Array:

    var arrayNames = ["apple", "apricot", "cucumber"]
    var secondArray = [fruitsData(name: "apple"),fruitsData(name: "apricot"), fruitsData(name: "mango"), fruitsData(name: "grapes"), fruitsData(name: "tomato"), fruitsData(name: "lichi"), fruitsData(name: "cucumber"), fruitsData(name: "brinjal")]
    var helperArray = [fruitsData]()
    
    for fruit in secondArray {
    
        if fruit.name != nil && !arrayNames.contains(fruit.name!){
            helperArray.append(fruit)
        }
    
    }
    
    secondArray = helperArray
    

The above will erase every element from secondArray whose name is contained by arrayNames. You should familiarise yourself with Map, Filter and Reduce.

Upvotes: 1

Luzo
Luzo

Reputation: 1374

I am on the phone so I can't check it, but this should do the trick

secondArray.filter { !firstArray.contains($0.name) }

Upvotes: 0

Related Questions