1

I've an array of strings. I need to sort the array based on array of keywords. The string containing max. number of keywords should come first and so on. Also, the string which contains max. no. of search keywords should come first than the number of occurrences of same search keyword. testArray should ignore case of searchTerms. If possible, you can ignore the strings which doesn't contain any search keywords in the result array.

var testArray = [
    "I am",
    "I am wrong and I don't know",
    "I am right and I know",
    "I don't know",
    "I do know"
  ],
  searchTerms = ["I", "right","know"];

$.each(searchTerms, function(index, term) {
  var regX = new RegExp(term, "i");
  testArray = $.map(testArray, function(item) {
    if (regX.test(item)) {
      return item;
    } else {
      return;
    }
  });
});

console.log(testArray);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

If you observe in the above code, the keywords are "I", "right","know". So the testArray results should be as below,

testArray = [
    "I am right and I know",    
    "I am wrong and I don't know",    
    "I don't know",
    "I do know",
    "I am"
  ]

string contain all the keywords come first and the other strings contain "I","know", So they come next and string "I am" comes last as it contains only "I" keyword.

Codepen

3 Answers 3

1

You can try something like this:

Idea:

  • Loop over data and criteria and create a Map that has both count and value.
  • Use a regex to match the string. This way you can do case-insensitive search.
  • Sort this Map based on count.
  • Return list of values.

function getMatchCountMap(data, criterias) {
  return data.reduce((countMap, curItem) => {
    var count = criterias.filter((criteria) => new RegExp(criteria, 'i').test(curItem) ).length;
    countMap.push({
      value: curItem,
      count: count
    });
    return countMap;
  }, [])
}

function sortBasedOnCount(data, criterias) {
  var map = getMatchCountMap(data, criterias);
  map.sort((a, b) => b.count - a.count);
  return map.map(x => x.value);
}

var testArray = [
    "I am",
    "I am wrong and I don't know",
    "I am right and I know",
    "I don't know",
    "I do know"
  ],
  searchTerms = ["I", "right","know"];
  
console.log(sortBasedOnCount(testArray, searchTerms))

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

Comments

1

You could try to create regex with those terms and sort by the number of matches, something like:

var testArray = [
    "I am",
    "i,i,i will come first i'm RIGHT i do KNOW",
    "I am wrong and I don't know",
    "I am right and I know",
    "I don't know",
    "I do know",
    "Something else"
  ],

  searchTerms = ["I", "right", "know"];


// (?:I)|(?:right)|(?:know)
const searchExp = new RegExp(searchTerms.reduce((acc, term) => acc ? `${acc}|(?:${term})` : `(?:${term})`, ''), 'gi');

const result = testArray.sort((a, b) => {
  const bMatch = b.match(searchExp) || [];
  const aMatch = a.match(searchExp) || [];

  return bMatch.length - aMatch.length;
});

console.log(result);

9 Comments

this fails in the case where there is no matching value. It is not able to read .length property for a null value (i.e. b.match(searchExp)).
@imixtron you are right I didn't considered that case
@J.Pichardo should ignore the case. Not working here
@Raviteja, hmm, i didn't see that in the question.
@Raviteja, haha, no worries, Anyway, example updated, check the i flag in the regexp
|
1

You could take an object for the counting and sort by the count of the string.

var array = [ "I am", "I am wrong and I don't know", "i am RIGHT and I know", "I don't know", "I do know", "i,i,i will come first i'm RIGHT i do KNOW"],
    search = ["I", "am","know"].map(v => v.toLowerCase()),
    count = array.reduce((c, k) => {
        var a = k.toLowerCase().split(/[^a-z0-9']+/),
            count = Object.create(null);

        a.forEach(w => count[w] = (count[w] || 0) + 1);
        c[k] = [0, 0];
        search.forEach(v => {
            c[k][0] += v in count;
            c[k][1] += count[v] || 0;
        });
        return c;
    }, Object.create(null));
  
array.sort((a, b) => count[b][0] - count[a][0] || count[b][1] - count[a][1]);

console.log(array);

6 Comments

Thanks for the quick response
you could normalize the search array with all lower case characters and take a lower case string for includes.
no. When the search words are "I", "am", "know", then "I am right and I know"should come first but, i,i,i will come first i'm RIGHT i do KNOW is coming first as count of i is more.
what should come actually first by searching for ["I", "am", "know"]?
|

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.