简体   繁体   中英

Add unique items to recyclerView from geoQuery

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 (eg base on theirs hashcode).

1) Objects comparing

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);
}

2) Collection

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));

Hint

You should name your class DiscoverItem not DiscoverItems . Because it is representing ONE item, not many.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM