Search code examples
javaandroidarraysarraylistcollections

How to pass only 1 element to ArrayAdapter and make sure it only displays unique items


Working on a project and have hit a mental wall trying to use a custom ArrayAdapter to display only unique elements from a class. I have a Class I've created that is simply made up of 3 strings. SongName, Category and Artist.

With that class I have created an ArrayList in my MainActivity. I would like to use that ArrayList in a few different activities. I've been able to use it in an ArrayAdapter that I created that displays all 3 fields in TextViews but I would like to create another ArrayAdapter for a different activity that only displays 1 of those fields and doesn't show the repeating items. So, for example, an ArrayAdapter that is only provided Category element and only displays the unique ones.

I'm having a hard time wrapping my head around how to only pass 1 element from the ArrayList to ArrayAdapter and make sure it doesn't display duplicates.

Fairly new to the android development and haven't been able to find an answer to this, so would appreciate it if you could help me or point me in the right direction, I would greatly appreciate it.

Edit Answer: was able to get it to work by adding this:

        ArrayList<Music> Library = MainActivity.music;


        ArrayList<String> artists = new ArrayList<String>();

        Log.i("MainActivity", String.valueOf(Library.size()));
        for (int i = 0; i<Library.size(); i++){
            String artist = Library.get(i).getArtist();
            if (!artists.contains(artist))
            artists.add(artist);

            Log.i("MainActivity", "artist");
        } 

Edit: including screenshot of the original ArrayList below. As you can see in my idea some of the categories and artists would repeat. I would like to create an activity that only displays the artists and another that only displays the categories. With the caveat being I only want the unique values to be displayed not duplicates & including github link.

github : https://github.com/Nimzr/Music-Player

Arraylist music from the Music class


Solution

  • By default the ArrayAdapter will create views for each array item in your ArrayList. To customise how the ArrayAdapter create views for your data you need to extend it and override its getView() method and use a different layout for each item like this

    NOTE: Im assuming Music is that class which has those 3 strings and you have created a new layout called unique_layout.xml which has only one TextView to display one you those strings.

    public class CustomArrayAdapter extends ArrayAdapter<Music> {
        public CustomArrayAdapter(Context context, ArrayList<Music> musicList) {
           super(context, 0, users);
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
    
           Music music = getItem(position); 
    
           // Here use that new layout that you created 
           if (convertView == null) {
              convertView = LayoutInflater.from(getContext()).inflate(R.layout.unique_layout, parent, false);
           }
    
           // Assign text to the TextView from that new layout
           TextView category = (TextView) convertView.findViewById(R.id.category);
           category.setText(music.category);
    
           return convertView;
       }
    }
    

    UPDATE: Remove Duplicates from ArrayList

    NOTE: The following methods removes duplicates and put the resulting list in a new ArrayList<String> which you can use as source of your ArrayAdapter. So you don't have to extend ArrayAdapter now.

    There are 3 common ways to remove duplicates from ArrayList

    1. Do it manually ( good luck in that )
    2. Use Streams ( only available for API 24: Nougat and greater )
    3. Use LinkedHashSet ( available since API 1 )

    Lets say you have array list defined as follows

    ArrayList<Music> musicList = new ArrayList<>();
    ArrayList<String> categoryList = new ArrayList<>();
    
    musicList.add(new Music("song1", "category1", "artist1"));
    musicList.add(new Music("song2", "category2", "artist2"));
    musicList.add(new Music("song3", "category3", "artist3"));
    musicList.add(new Music("song4", "category2", "artist4"));
    musicList.add(new Music("song5", "category1", "artist1"));
    musicList.add(new Music("song6", "category4", "artist5"));
    musicList.add(new Music("song7", "category3", "artist2"));
    

    Option 2 : Using Streams

    What I'm doing here is that I'm extracting only the category field using the map() method and removing duplicates using the distinct() method then finally collecting all this modification in categoryList. If you need to extract unique artist, just change music.category to music.artist

    EZ !

    NOTE: After the map() method we are now dealing with array of strings so distinct() uses the equals() and hashCode() methods of the String class to find and remove duplicates. If you are using distinct() directly on your musicList which is an array of Music class then your Music class should override and provide implementation of equals() and hashCode() methods.

    Read more about them here or here

    categoryList = musicList.stream()
                  .map((music)-> music.category)
                  .distinct()
                  .collect(Collectors.toCollection(ArrayList::new));
    
    Log.d("CATEGORY LIST" ,  categoryList.toString())
    
    //LOG Output 
    CATEGORY LIST: [category1, category2, category3, category4]
    

    Option 3 : Using LinkedHashSet

    This method is rather simple. As before first extracting category fields and storing them into categoryList. Creating LinkedHashSet will automatically remove duplicate values just like the distinct() method did when using streams. Afterwards just clearing the categoryList and updating it with unique values from the hash set.

    musicList.forEach((music) -> {
        categoryList.add(music.category);
    });
    
    LinkedHashSet<String> uniqueCategoryHashSet = new LinkedHashSet(categoryList);
    
    categoryList.clear();
    categoryList.addAll(uniqueCategoryHashSet);
    
    Log.d("CATEGORY LIST" ,  categoryList.toString())
    
    //LOG Output 
    CATEGORY LIST: [category1, category2, category3, category4]
    

    Now by no means I would mark Option 3 as an efficient solution. Maybe you can do better doing it manually if you can't use streams due to API restriction. But this gets the job done.