Problem With Filter

I'm trying to filter my players using searchBar with scope in a collectionview using a switch statement and my case 0 works but the issue is my other cases I can type into the searchBar but I can't seem to filter (nothing shows up) nor can I delete all the characters when I type.

I have 2 arrays my cPlayerArr is my original array when data is recieved and my allTextArr is my filtered array when any kind of filtering is done.

Code Block
   func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
    allTextArr = cPlayerArr.filter({ player -> Bool in
      switch searchBar.selectedScopeButtonIndex {
      case 0:
        if searchText.isEmpty {
          fetchAllPlayers()
        } else {
        return
          player.yahooName!.lowercased().contains(searchText.lowercased())
        }
      case 1:
        if searchText.isEmpty {
          fetchForwards()
        } else {
          print(searchText)
          return player.yahooName!.lowercased().contains(searchText.lowercased()) && player.position == "C" &&
            player.position == "RW" && player.position == "LW"
        }
      case 2:
        if searchText.isEmpty {
          fetchDefense()
        } else {
          return player.yahooName!.lowercased().contains(searchText.lowercased()) && player.position == "D"
        }
      case 3:
        if searchText.isEmpty {
          fetchGoalies()
        } else {
          return player.yahooName!.lowercased().contains(searchText.lowercased()) && player.position == "G"
        }
      default:
        return false
      }
      return true
    })
    collections.reloadData()
  }


Code Block
   func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange: Int) {
    if (searchBar.selectedScopeButtonIndex == 0) {
      cPlayerArr.removeAll()
      allTextArr.removeAll()
      fetchAllPlayers()
    } else if (searchBar.selectedScopeButtonIndex == 1) {
      allTextArr.removeAll()
      fetchForwards()
    } else if (searchBar.selectedScopeButtonIndex == 2) {
      allTextArr.removeAll()
      fetchDefense()
    } else {
      allTextArr.removeAll()
      fetchGoalies()
    }
  }

Code Block
   func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    if (searchBar.text != "" || searchBar.selectedScopeButtonIndex > 0) {
      return allTextArr.count
    } else {
      return cPlayerArr.count
    }
  }


Can you show all the definitions of cPlayerArr, allTextArr, fetchAllPlayers(), fetchForwards(), fetchDefense(), fetchGoalies() and all the dataSource methods?
They may not be implemented consistently.
In case 1, you have a condition which is always false:

Code Block
return player.yahooName!.lowercased().contains(searchText.lowercased()) && player.position == "C" && player.position == "RW" && player.position == "LW"

player.position cannot be simultaneously the 3 different values.

May be you need:
Code Block
         return player.yahooName!.lowercased().contains(searchText.lowercased()) && (player.position == "C" || player.position == "RW" || player.position == "LW")


Thanks Claude. I solved my filtering with the other scopes but the thing that lingers is I can't delete all the characters in the searchBar unless it's on scope 0 and once I delete characters I can't switch to any scope unless it's scope 0.


OOPer says

Can you show all the definitions of cPlayerArr, allTextArr, fetchAllPlayers(), fetchForwards(), fetchDefense(), fetchGoalies() and all the dataSource methods?
They may not be implemented consistently.

Code Block
var cPlayerArr = [CurrentPlayers]()
   
//filters players based on scope
var allTextArr = [CurrentPlayers]()
func fetchForwards() {
    do {
      let request = CurrentPlayers.fetchRequest() as NSFetchRequest<CurrentPlayers>
      let forwards = NSPredicate(format: "position CONTAINS 'RW' OR position CONTAINS 'LW' OR position CONTAINS 'C'")
      request.predicate = forwards
      self.allTextArr = try context.fetch(request)
      DispatchQueue.main.async {
        self.collections.reloadData()
      }
    }
    catch {
       
    }
  }
   
  func fetchDefense() {
    do {
      let request = CurrentPlayers.fetchRequest() as NSFetchRequest<CurrentPlayers>
      let defense = NSPredicate(format: "position CONTAINS 'D'")
      request.predicate = defense
      self.allTextArr = try context.fetch(request)
      DispatchQueue.main.async {
        self.collections.reloadData()
      }
    }
    catch {
       
    }
  }
   
  func fetchGoalies() {
    do {
      let request = CurrentPlayers.fetchRequest() as NSFetchRequest<CurrentPlayers>
      let goalies = NSPredicate(format: "position CONTAINS 'G'")
      request.predicate = goalies
      self.allTextArr = try context.fetch(request)
      DispatchQueue.main.async {
        self.collections.reloadData()
      }
    }
    catch {
       
    }
  }
     func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "colcell", for: indexPath) as! PlayerColCell
      if (searchBar.text != "" || searchBar.selectedScopeButtonIndex > 0) {
        item = allTextArr[indexPath.row]
        cell.layer.borderColor = UIColor.black.cgColor
        cell.layer.borderWidth = 0.5
        cell.contentView.backgroundColor = UIColor(red: 102/256, green: 255/256, blue: 255/256, alpha: 0.6)
        cell.update(with: item)
        return cell
      } else {
        item = cPlayerArr[indexPath.row]
        cell.layer.borderColor = UIColor.black.cgColor
        cell.layer.borderWidth = 0.5
        cell.contentView.backgroundColor = UIColor(red: 102/256, green: 255/256, blue: 255/256, alpha: 0.6)
        cell.update(with: item)
        return cell
      }
    }


Thanks Claude. I solved my filtering with the other scopes

How did you solve ? By correction the logical expression as I proposed ?

but the thing that lingers is I can't delete all the characters in the searchBar unless it's on scope 0 and once I delete characters I can't switch to any scope unless it's scope 0. 

What happens when you delete all char in search bar ?
Can you delete all but the last ?

One difference I see, is that in case 0 you clear the allTextArr array (what a bizarre name for [CurrentPlayers] array) and you reload them in other cases.

Could you show the code of:
fetchAllPlayers()
Thanks for showing your code.

Checking your dataSource methods, your UICollectionView (collections) shows allTextArr when searchBar.selectedScopeButtonIndex > 0.
But in your searchBar(_:textDidChange:), you update allTextArr from cPlayerArr.

Generally:
  • You need to re-fetch when selectedScopeButtonIndex is changed

  • You need to re-filter when searchText is changed

But your current searchBar(_:textDidChange:) mixes up two different things -- re-fetch and re-filter.

I would write them as follows:
Code Block
// Array of fetched `CurrentPlayers` when `selectedScopeButtonIndex > 0`
var origPlayerArr = [CurrentPlayers]()
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
updateFilteredArray(searchText: searchText)
collections.reloadData()
}
private func updateFilteredArray(searchText: String) {
let origArray: [CurrentPlayers]
if searchBar.selectedScopeButtonIndex > 0 {
origArray = origPlayerArr
} else {
origArray = cPlayerArr
}
allTextArr = origArray.filter { player -> Bool in
if searchText.isEmpty {
return true
} else {
return
player.yahooName!.lowercased().contains(searchText.lowercased())
}
}
}
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange: Int) {
if (searchBar.selectedScopeButtonIndex == 0) {
cPlayerArr.removeAll()
allTextArr.removeAll()
fetchAllPlayers()
} else if (searchBar.selectedScopeButtonIndex == 1) {
allTextArr.removeAll()
fetchForwards()
} else if (searchBar.selectedScopeButtonIndex == 2) {
allTextArr.removeAll()
fetchDefense()
} else {
allTextArr.removeAll()
fetchGoalies()
}
updateFilteredArray(searchText: searchBar.text ?? "")
collections.reloadData()
}


And modify fetch methods fetchForwards(), fetchDefense() and fetchGoalies() as:
Code Block
func fetchForwards() {
do {
let request = CurrentPlayers.fetchRequest() as NSFetchRequest<CurrentPlayers>
let forwards = NSPredicate(format: "position CONTAINS 'RW' OR position CONTAINS 'LW' OR position CONTAINS 'C'")
request.predicate = forwards
self.origPlayerArr = try context.fetch(request) //<-
} catch {
print(error)
//...
}
}
// Same for `fetchDefense()` and `fetchGoalies()`...


Claude31 says

What happens when you delete all char in search bar ? 
Can you delete all but the last ?




One difference I see, is that in case 0 you clear the allTextArr array (what a bizarre name for [CurrentPlayers] array) and you reload them in other cases.




Could you show the code of:
fetchAllPlayers()

I list my current issues with OOper's code and I called it allTextArr because I use text to filter but I changed it to filteredArr so it's more clear. Here is the code for fetchAllPlayers()

Code Block
   func fetchAllPlayers() {
    parseJSON {
      self.collections.reloadData()
    }
  }


OOper says

Thanks for showing your code.



Checking your dataSource methods, your UICollectionView (collections) shows allTextArr when searchBar.selectedScopeButtonIndex > 0.
But in your searchBar(_:textDidChange:), you update allTextArr from cPlayerArr.

Generally:
You need to re-fetch when selectedScopeButtonIndex is changed
You need to re-filter when searchText is changed
But your current searchBar(_:textDidChange:) mixes up two different things -- re-fetch and re-filter.

I would write them as follows:


Okay I tried your solution but there are issues with selectedScope > 0,
  1. When I filter for a specific player in selectedScope > 0, I see multiple cells of the same player instead of one.

  2. When I delete all character it should clear and reload one of those fetch methods instead of giving multiple cells of the same player.

  3. After I delete all characters and when I try to start typing again in the searchBar I see no cells shown.

Okay I tried your solution but there are issues with selectedScope > 0,

I cannot reproduce the issue you have described. You may be doing something wrong somewhere.
OOPer says

I cannot reproduce the issue you have described. You may be doing something wrong somewhere.

I copied it exactly how it was written and it does fix a few things while giving me a few more issues.

I copied it exactly how it was written and it does fix a few things while giving me a few more issues.

You may be doing something strange beyond my guess where you have not shown yet.
Have you checked the syntax of predicates ?

Isn't it
      let defense = NSPredicate(format: "position CONTAINS [D]")

OOper says

You may be doing something strange beyond my guess where you have not shown yet.


Okay I will look at my code again, I will recopy it.


Claude31 says

Isn't it let defense = NSPredicate(format: "position CONTAINS [D]")

I don't think so, I think it's "position CONTAINS 'D'".

Okay I will look at my code again, I will recopy it.

Thanks.
I have copied all the code you have shown in this thread, modified some methods as I have shown.
(And I needed to fill many parts to make the project build and run.)
And then replaced some parts to provide some test data and I could not reproduce the issue you have described:

there are issues with selectedScope > 0,

  1. When I filter for a specific player in selectedScope > 0, I see multiple cells of the same player instead of one.

  2. When I delete all character it should clear and reload one of those fetch methods instead of giving multiple cells of the same player.

  3. After I delete all characters and when I try to start typing again in the searchBar I see no cells shown.

If you could show some info why all those things were happening, I would try to solve the issue,
but currently I have no clue what's going on.
Okay I fixed some of my issues, the only issue I now have is the one where multiple cells of the same player are shown when you type in the searchBar. I think it has to do with the updateFilteredArray method below. If we get a specific player we need to basically erase any duplicates. This only happens when selectedScope > 0.

Code Block
 func updateFilteredArray(searchText: String) {
   let filterArray: [CurrentPlayers]
   if searchBar.selectedScopeButtonIndex > 0 {
     filterArray = filteredArr
   } else {
     filterArray = cPlayerArr
   }
   filteredArr = filterArray.filter { player -> Bool in
     if searchText.isEmpty {
       return true
     } else {
print(filteredArray.count)
       return
         player.yahooName!.lowercased().contains(searchText.lowercased())
     }
   }
 }


One of the ways I'm trying to remove duplicates by turning it into a set then back into an array when called but that does not seem to work. I could use some more help.

Code Block
   func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    let noDArr = Array(Set(filteredArr))
    if (searchBar.text != "" || searchBar.selectedScopeButtonIndex > 0) {
      return noDArr.count
    } else {
      return cPlayerArr.count
    }
  }

Problem With Filter
 
 
Q