1

I have to implement a search functionality in an app I am currently working on. However, the problem is how we display the results. For Example,

const queries = ['revised sales report','sales report',  'revision: sales investors report']

function filterResults(term) {
  const search = queries.filter(item =>item.toLowerCase().includes(term.toLowerCase()));
  return search;
  }

  console.log(filterResults('sales report'))

The above function gives me

[
"revised sales report",
"sales report"
]

as results. But however, the desired output should be,

[
"sales report",
"revised sales report",
"revision: sales investors report"
]

That is, my first result should always start with the exact search string i.e, even though sales report is the second item in the array, that is the exact match so it should appear first, second result can contain string at any place, i.e, the first item in the array has the search term but it starts with some other word and hence it should appear second. and subsequent result can have each input in different places of the sentence , i.e, the third item in the array has both the term sales and report but it is in disconnected and appears in different part of the sentence so it should appear after the second result. But the function I made above completely ignores the third result.

I have no clue how to approach this. I just started working 3 months ago. Any pointers on how to approach would be helpful.

4
  • 3
    You specific example will actually return nothing []. Post a better example that makes sense so we can understand what is wrong. Commented Sep 29, 2020 at 11:18
  • Could you please elaborate it a bit more on filtering logic and provide some inputs and given/expected outputs from the filter function... Commented Sep 29, 2020 at 11:25
  • @GabrielePetrioli Sorry, I just changed it. Working in multiple tabs so got confused. Commented Sep 29, 2020 at 11:28
  • one approach might be that your filterResults() function returns an array of objects shaped as { result: 'foo', priority: 1 }, where you set the priority according to your need (1 if term === item, 2 if item.indexOf(term) > -1, 3 if item contains any of the words in term - here you will have to split term and loop through the resulting array). Then you can simply sort the results by their priority Commented Sep 29, 2020 at 11:32

3 Answers 3

1

From your description you seem to want to rank results based on 3 different criteria

  1. result === query
  2. result includes query
  3. results includes all words of query

So you search function, will have to search for each type of match, and assign a rank to the result.

A naive approach

const queries = ['revised sales report', 'sales report', 'revision: sales investors report']


const matchWholeString = (value, term) =>
  value === term;
const matchIncludesString = (value, term) =>
  value.includes(term);
const matchIncludesAllWords = (value, term) =>
  term.split(/\s+/).every(word => value.includes(word));

function filterResults(term) {
  const lowercaseTerm = term.toLowerCase();
  const search = queries.map(query => {
    const lowercaseQuery = query.toLowerCase();
    if (matchWholeString(lowercaseQuery, lowercaseTerm)) {
      return {
        query,
        rank: 1000
      };
    }
    if (matchIncludesString(lowercaseQuery, lowercaseTerm)) {
      return {
        query,
        rank: 100
      }
    }
    if (matchIncludesAllWords(lowercaseQuery, lowercaseTerm)) {
      return {
        query,
        rank: 10
      }
    }
    return {
      query,
      rank: 0
    }
  }).filter(({
    rank
  }) => rank).sort((a, b) => a.rank < b.rank).map(({
    query
  }) => query);
  return search;
}

console.log(filterResults('sales report'))

Sign up to request clarification or add additional context in comments.

Comments

1

Assuming your actual search string was 'sales report' and your intention was to return

  • exact match (hereafter case-insensitively) as a first search output
  • partial match (where searched string is a substring of some query item)
  • vague match (where both 'sales' and 'report' strings are present in arbitrary order

You may do the following:

const queries = ['revised sales report','sales report',  'revision: sales investors report'],

      searchedString = 'sales report',
      
      search = (arr, str) => {
        const exactMatch = arr.find(s => s.toLowerCase() == str.toLowerCase()),
              partialMatches = arr.filter(s => s.toLowerCase().includes(str.toLowerCase())),
              vagueMatches = arr.filter(s => 
                 str
                  .toLowerCase()
                  .split(' ')
                  .every(w => s.toLowerCase().includes(w))
              )
        return [...new Set([exactMatch, ...partialMatches, ...vagueMatches])]            
      }
      
console.log(search(queries, searchedString))     

p.s. this, of course, does not take punctuation into account (but may be adjusted to do that either), but surely gives an idea on how to approach your problem.

Comments

0

This is how the includes function works. it simply checks whether the value you provided which is 'sales report' let's say, exist in the string you are checking against which it does in the first two strings in the list, but not in the last one. includes checks the exact value, it won't do a fuzzy search for you.

You can use something like https://fusejs.io/ to achieve what you want. It can do a fuzzy search and will provide you with a score for each found value so you can use that to order the results in the way you described.

an example from their documentation

const list = ["Old Man's War", 'The Lock Artist']
const options = {
  includeScore: true
}

const fuse = new Fuse(list, options)

const result = fuse.search('od man')

result

[
  {
    "item": "Old Man's War",
    "refIndex": 0,
    "score": 0.35
  }
]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.