user6539552
user6539552

Reputation: 1451

Filtering two list with a specific properties in swift

Let say I have two different struct

struct ItemA:Hashable, Codable, Identifiable {
    var id: Int
    var itemBId: [Int]
}

struct ItemB:Hashable, Codable, Identifiable {
    var id: Int
    var isSelected: Bool
}

Assume that I have a list of ItemA and ItemB objects called "itemAs" and "itemBs" respectively.

Some of the itemBs object may have isSelected = true.

I therefore want to filter and show only the ItemA object, with a itemB index, which is in the list of itemBs's true case.

var filteredItemAbyItemB: [Bird] {
    
    let itemBFilter = modelData.itemBs.filter {$0.isSelected})
    
    modelData.itemAs.filter {
        itemA -> Bool in
        itemBFilter.contains(itemA.itemBId)  // fail here
    }
}

May I know how to do that?

Example ItemB objects:

ib1 = {1, true}
ib2 = {2, false}
ib3 = {3, true}

itemBFilter should contain objects ib1 and ib3

ItemA objects:

ia1 = {1, [1,2]} // should appear in the final list, cause has itemBid = 1
ia2 = {2, [1]}   // should appear in the final list, cause has itemBid = 1
ia3 = {3, [2]}
ia4 = {4, [2,3]} // should appear in the final list, cause has itemBid = 3

The final filtered result should contains objects ia1, ia3, and ia4

Upvotes: 0

Views: 192

Answers (2)

fruitcoder
fruitcoder

Reputation: 1228

It's best to use Sets for this to avoid multiple contains:

let selectedBIds = modelData.itemBs
  .filter { $0.isSelected }
  .map(\.id)

let filteredAWithAllBsSelected = modelData.itemAs.filter { Set($0.itemBId).isSubset(of: selectedBIds) }
let filteredAWithAtLeastOneBSelected = modelData.itemAs.filter { !Set($0.itemBId).intersection(selectedBIds).isEmpty }

Note that you didn't specify in your question whether you wanted all b's to be selected, but your expected outcome made that clear. Maybe you also have to think about empty b's so do you want a's without any b's to also be in your result? It comes down to whether you expect at least one b to be selected or not all of the b's to not be selected

Upvotes: 0

I think you can try this

    // Your items
    let itemsB = [ItemB(id: 1, isSelected: true),
                  ItemB(id: 2, isSelected: false),
                  ItemB(id: 3, isSelected: true)]
    let itemsA = [ItemA(id: 1, itemBId: [1, 2]),
                  ItemA(id: 2, itemBId: [1]),
                  ItemA(id: 3, itemBId: [2]),
                  ItemA(id: 4, itemBId: [2, 3])]
    // I filter and map itemsB to get selected Ids
    let itemsBselectedIds = itemsB.filter { $0.isSelected }.map { $0.id }
    // Then I filter itemsA to verify if the itemA has a selected Id from itemsB
    let filter = itemsA.filter { itemA in
        itemsBselectedIds.contains(where: { itemA.itemBId.contains($0) })
    }

In your code you try to verify if itemsB contains itemA.id, but itemsB is an array of ItemB not an array of id [Int]

Upvotes: 1

Related Questions