Extract the item from the list which has highest number of matches from given String

I have 2 Arraylists in my app. First arraylist is of Object type which contains a list of questions. Now this list of questions have a field named "Keywords". This is a String but can contain comma separated keywords.

Now I have a text field where user can search question based on these keywords.Issue that I am facing is that I want to filter out question from the question list according to the number of keyword matches.

For eg. User entered 3 comma separated keywords in the search text field. What I want now is if all 3 keyword matches with some value in the question list then I have to return those elements only. This part is easy and I can do it.

But if we don't get any exact match in the list, then I have to find that item which has the maximum keyword match i.e. if 2 out of 3 keywords from the comma separated String matches from some item in the list, then I have to return that item as result.

Value Stored in List :-

a) Hi, Hello, Hola, Bonjour.

b) Hi, Hello

c) Hi

Value entered in the search text :-

Hi, Hello, Hola

Now in response I want only the first element as it has 3 keywords matching from what user entered. I am unable to figure out how to do this. Moreover I am fetching this questions list from sqlite database, so if this can be done with some sql queries then I am ready for that thing too.

This is my current code for filter method

 public ArrayList<QuestionAnswerModal> filter(String keyword,boolean isQuestionSearch) {
    ArrayList<QuestionAnswerModal> arrayList = new ArrayList<>();

    if (!isQuestionSearch) {
        for (QuestionAnswerModal modal : questionAnswerArrayList) {
            if (modal.getKeyword().equalsIgnoreCase(keyword)) {
                arrayList.add(modal);
            }else{
                ArrayList<String> keywords=new ArrayList<>();
                String[]word=modal.getKeyword().split(",");

            }
        }
        if (arrayList.size() > 0) {
            lvQuestionAnswer.invalidate();
            QuestionAnswerAdapter questionAnswerAdapter = new QuestionAnswerAdapter(arrayList, MainActivity.this, MainActivity.this, MainActivity.this);
            lvQuestionAnswer.setAdapter(questionAnswerAdapter);
            dialog.dismiss();

        } else {
            Toast.makeText(MainActivity.this, "No records found", Toast.LENGTH_SHORT).show();
        }
        return arrayList;
    }else{
        for (QuestionAnswerModal modal : questionAnswerArrayList) {
            if (modal.getQuestion().equalsIgnoreCase(keyword)) {
                arrayList.add(modal);
            }
        }
        if (arrayList.size() > 0) {
            lvQuestionAnswer.invalidate();
            QuestionAnswerAdapter questionAnswerAdapter = new QuestionAnswerAdapter(arrayList, MainActivity.this, MainActivity.this, MainActivity.this);
            lvQuestionAnswer.setAdapter(questionAnswerAdapter);
            dialog.dismiss();

        } else {
            Toast.makeText(MainActivity.this, "No records found", Toast.LENGTH_SHORT).show();
        }
        return arrayList;
    }
}

1 answer

  • answered 2018-07-12 10:20 Eric Green

    I leave it to you as an exercise to figure out how this solution works, but feel free to ask any questions you wish.


    Java 7 solution:

    import java.util.*;
    
    import static org.apache.commons.lang3.StringUtils.trimToEmpty;
    
    public class MaxMatchFinder {
    
        public static void main(String[] args) {
    
            Map<String, Set<String>> tagsByName = new HashMap<>();
            tagsByName.put("a", new HashSet<>(Arrays.asList("Hi", "Hello", "Hola", "Bonjour")));
            tagsByName.put("b", new HashSet<>(Arrays.asList("Hi", "Hello")));
            tagsByName.put("c", new HashSet<>(Arrays.asList("Hi")));
    
            String searchText = "Hi, Hello, Hola";
    
            String[] tagsToFind = searchText.split(",");
    
            Map<String, Integer> matchCountsByEntryName = new HashMap<>();
    
            for (String tagToFind : tagsToFind) {
                for (String entryName : tagsByName.keySet()) {
                    Set<String> tags = tagsByName.get(entryName);
                    if (tags.contains(trimToEmpty(tagToFind))) {
                        Integer count = matchCountsByEntryName.get(entryName);
                        Integer incrementedCount = count == null ? 1 : count + 1;
                        matchCountsByEntryName.put(entryName, incrementedCount);
                    }
                }
            }
    
            List<Map.Entry<String, Integer>> sortedEntries = new ArrayList<>(matchCountsByEntryName.entrySet());
            Collections.sort(sortedEntries, new Comparator<Map.Entry<String, Integer>>() {
                @Override
                public int compare(Map.Entry<String, Integer> e1, Map.Entry<String, Integer> e2) {
                    return e2.getValue().compareTo(e1.getValue());
                }
            });
    
            Map.Entry<String, Integer> entryWithMostMatches = sortedEntries.get(0);
    
            System.out.printf("Of the entries to be searched," +
                " entry \"%s\" contains the most matches (%d).\n",
                    entryWithMostMatches.getKey(), entryWithMostMatches.getValue());
        }
    }
    

    Java 8 solution:

    import java.util.*;
    import java.util.stream.Collectors;
    
    import static org.apache.commons.lang3.StringUtils.trimToEmpty;
    
    public class MaxMatchFinder {
    
        public static void main(String[] args) {
    
            Map<String, Set<String>> tagsByName = new HashMap<>();
            tagsByName.put("a", new HashSet<>(Arrays.asList("Hi", "Hello", "Hola", "Bonjour")));
            tagsByName.put("b", new HashSet<>(Arrays.asList("Hi", "Hello")));
            tagsByName.put("c", new HashSet<>(Arrays.asList("Hi")));
    
            String searchText = "Hi, Hello, Hola";
    
            String[] tagsToFind = searchText.split(",");
    
            Map<String, Integer> matchCountsByEntryName = new HashMap<>();
    
            Arrays.stream(tagsToFind)
                    .forEach(tagToFind -> {
                        for (String entryName : tagsByName.keySet()) {
                            Set<String> tags = tagsByName.get(entryName);
                            if (tags.contains(trimToEmpty(tagToFind))) {
                                matchCountsByEntryName.compute(entryName, (k, v) -> v == null ? 1 : v + 1);
                            }
                        }
                    });
    
            List<Map.Entry<String, Integer>> sortedEntries = matchCountsByEntryName.entrySet().stream()
                    .sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue()))
                    .collect(Collectors.toList());
    
            Map.Entry<String, Integer> entryWithMostMatches = sortedEntries.get(0);
    
            System.out.printf("Of the entries to be searched," +
                " entry \"%s\" contains the most matches (%d).\n",
                    entryWithMostMatches.getKey(), entryWithMostMatches.getValue());
        }
    }