I created an app that shows items of users near me.
I find the people who near me by using geoQuery
and if they are near me, I search in the database what items they have and I populate a RecyclerView
with them.
I do so by using:
List<DiscoverItems> items = new ArrayList<>();
GeoQuery geoQuery = geoFire.queryAtLocation(new GeoLocation(Lat, Lon), Radius);
geoQuery.addGeoQueryEventListener(new GeoQueryEventListener() {
@Override
public void onKeyEntered(String key, GeoLocation location) {
String UserID = key;
if (!UserID.equals(auth.getUid())) {
db.collection("Users").document(UserID).collection("MyItems").get().addOnCompleteListener(task_count -> {
if (task_count.isSuccessful()) {
for (DocumentSnapshot document_near : task_count.getResult()) {
items.add(new DiscoverItems(document_near.getString("ItemID")));
}
}
});
}
discoverItemAdapter = new DiscoverItemAdapter(items, listener);
rv_NearMeItems.setAdapter(discoverItemAdapter);
}
@Override
public void onKeyExited(String key) {
}
@Override
public void onKeyMoved(String key, GeoLocation location) {
}
@Override
public void onGeoQueryReady() {
}
@Override
public void onGeoQueryError(DatabaseError error) {
}
});
However, my problem is if some users have the same item, because then it will appear twice in the recyclerView while I had liked it to have unique items.
Is there any solution where I can first check my items list for duplicate and only then to add them?
I tried to do the following:
List<DiscoverItems> items= new ArrayList<>();
Set<DiscoverItems> itemkset = new HashSet<>();
GeoQuery geoQuery = geoFire.queryAtLocation(new GeoLocation(Lat, Lon), Radius);
geoQuery.addGeoQueryEventListener( new GeoQueryEventListener() {
@Override
public void onKeyEntered(String key, GeoLocation location) {
String UserID = key;
if (!UserID.equals( auth.getUid() )) {
db.collection( "Users" ).document( UserID ).collection( "MyItems" ).get().addOnCompleteListener( task_count -> {
if (task_count.isSuccessful()) {
for (DocumentSnapshot document_near : task_count.getResult()) {
itemset.add( new DiscoverItems( document_near.getString( "ItemID" ) ) );
}
}
} );
}
List<DiscoverItems> itemsWithoutDuplicates = new ArrayList<>(new HashSet<>(itemset));
for (DiscoverItems item: itemsWithoutDuplicates){
items.add(item);
}
discoverItemAdapter = new DiscoverItemAdapter(items, listener);
rv_NearMeItems.setAdapter(discoverItemAdapter);
}
But it doesn't work since if for example, I have 2 users, onKeyEntered
will be called twice. For the first user it will add to itemset
let say 4 items which are not identical and everything will be ok. Then it will call the second user which will add also 3 items to itemset which are not identical and it will be ok.
Am I missing something? Because with the code above I'm getting multiple items.
Thank you
I don't know the complexity of your objects, but you can try change from List<>
int to Set<>
. In general Set
is collection that contains no duplicate elements (e.g. base on theirs hashcode).
First of all you have to override equals()
and hashCode()
in DiscoverItems
class to work with hash-based collections (like HashSet
).
You can generate those method in Android Studio and choose base on which fields you can say that "those two objects are that same". Sometimes it's not necessary to compare all of the fields (you can use for example only name or some ID).
You your case, you have to compare this field which you are passing to constructor of DiscoverItems
class. It can look like there:
@Override
public boolean equals(Object object) {
if (this == object) return true;
if (object == null || getClass() != object.getClass()) return false;
DiscoverItems that = (DiscoverItems) object;
return Objects.equals(itemID, that.itemID);
}
@Override
public int hashCode() {
return Objects.hash(itemID);
}
So change from:
List<DiscoverItems> items = new ArrayList<>();
in to:
Set<DiscoverItems> items = new HashSet<>();
When you need checking in one place, you can convert:
List ---> Set ---> List
for example:
Set<DiscoverItems> set = new HashSet<>(items);
if (items.size() == set.size()) {
// No duplicates, use original list
} else {
// Duplicates removed, use new list
List<DiscoverItems> itemsWithoutDuplicates = new ArrayList<>(set);
}
or without extra condition:
Set<DiscoverItems> set = new HashSet<>(items);
List<DiscoverItems> itemsWithoutDuplicates = new ArrayList<>(set);
or as one-liner:
List<DiscoverItems> itemsWithoutDuplicates = new ArrayList<>(new HashSet<>(items));
You should name your class DiscoverItem
not DiscoverItems
. Because it is representing ONE item, not many.