0

I have the following title: Banana & Walnut Cinnamon Bread

I've created a search function which allows the user to type in free text, when I enter Banana Bread the above item with the title: Banana & Walnut Cinnamon Bread will not show purely because I'm using indexOf, however if the title was : Banana Bread & Walnut Cinnamon then it would work because 'Banana Bread' is next to one another.

Can someone suggest a javscript function which will allow me to search the complete string and pick out the words that match what I've typed in?

This was the original:

  if (searchDataList[i].ttl.toLowerCase().indexOf(freeText) > -1) {
    addItem = true;
  } 
  else {
    addItem = false;
  }
Cœur
  • 37,241
  • 25
  • 195
  • 267
Code Ratchet
  • 5,758
  • 18
  • 77
  • 141

4 Answers4

0

Try splitting your input string and then do the search for each individual word. In this way you can get results for different combination of words.

sandyclone
  • 149
  • 4
  • 1
    You should consider adding this as a comment rather than an answer, unless you intend to expand on it. – Xotic750 Jan 18 '16 at 02:57
  • I've already thought of splitting the string and searching, however our list is quite long and doing a foreach to loop through it all could be time consuming. – Code Ratchet Jan 18 '16 at 02:58
  • You should always get your code working first, and then worry about performance if necessary. – Xotic750 Jan 18 '16 at 03:01
0

You should first turn your searchDataList (I guess it's currently an array) into regexp, this way:

  • assuming searchDataList is ['aa', 'bb', 'cc']
  • make the corresponding regexp /(aa|bb|cc)/g

Then you can submit the entered words to this regexp: the returned array lists the words found.

cFreed
  • 4,404
  • 1
  • 23
  • 33
  • @MillieSmith How should understand? – cFreed Jan 18 '16 at 03:23
  • Deleted my comment. Sorry. I should've held my cool. It's just that people try to use regex for everything, and it's rarely the best answer. – Millie Smith Jan 18 '16 at 03:26
  • @MillieSmith Yep And what do you think about this case? :D – cFreed Jan 18 '16 at 03:29
  • I don't think it's the best solution :P. The regex is just going to tell you *if* there's a match, not what was matched. Plus it really doesn't make anything faster since it still has to check all the cases until it finds one. He might as well just split on spaces and loop over his array. That way it's nore readable and maintainable. – Millie Smith Jan 18 '16 at 03:40
  • @MillieSmith I agree that "it still has to check all the cases" but it should be faster because internal. On the other hand, unless I've forgotten something, using the `g` modifier will cause the result to be an array of the matching words (if any). – cFreed Jan 18 '16 at 03:51
  • It'll show all matches in the search string, not the titles. – Millie Smith Jan 18 '16 at 04:04
  • @MillieSmith I don't understand: isn't it exactly what the OP looks for? He said: "search the complete string and pick out the words that match what I've typed in". – cFreed Jan 18 '16 at 04:08
0

The simple way to do it is to split your search text into individual words and check each one...

var searchWords = freeText.split(' ');

searchWords = CleanWords(searchWords); // This removes words like '&', '-', 'the' 

for(var i = 0; i < searchDataList.length; i++){
  for (var n = 0; n < searchWords.length; n++){
    var word = searchWords[n].toLowerCase();
    var title = searchDataList[i].ttl.toLowerCase();

    if (title.indexOf(word) > -1){
      // addItem = true;
    } 
  }
}

If you don't want to do that then you could try pre build an index and check if the search words are in the index. Pseudo code below...

// Prebuild index
var index = {};
foreach(var item in searchDataList){
  var words = item.ttl.split(' ');
  words = CleanWords(words);   // This removes words like '&', '-', 'the' etc

  foreach(var word in words){
    word = word.toLowerCase();
    if (index[word]){
      index[word].Push(item.no);  // Build up array of search term indexes that contain this word.
    } else {
      index[word] = [item.no];
    }
  }
}

// Perform search
var searchWords = freeText.split(' ');
searchWords = CleanWords(searchWords);
foreach(var word in searchWords){
  var matches = index[word];
  foreach(var match in matches){
    // Do something with matches
    var matchedItem = searchDataList[match];
  }
}

Also have a look at the following answer for more discussion on speed of accessing items by key on an object in JavaScript... Array vs. Object efficiency in JavaScript

You could also consider using var index = new Map(); rather than an object (I'm not sure if it is faster or not though).

Community
  • 1
  • 1
Quantumplate
  • 1,104
  • 8
  • 15
0

I know that is an old post, but as i was searching for the same functionality, and after a lot of experimenting i ended up with this:

//text = string to search into it
//searchWords = array of given words to be found
//e.g. searchWords = ['word1','word2','word3']
function multiSearchAnd(text, searchWords) {

  //count how many words to be searched
  let totalWords = searchWords.length;
  let found_words = 0;
  let start = 0;

  //while start counter is not equal (less than) totalWords (searchWords.length)
  while (start != totalWords) {
    //check each one word if exists in text
    if (text.match(new RegExp(searchWords[start], "i"))) {
      //if you find a match add it to found_words counter
      found_words++;
    }
    //increase start counter by one in order to search the next word at searchWords array
    start++;
    //another way but it did't worked was to shift the first element of the array
    //somehow, when searchWords array got empty for the very first time, it did never got
    //element again.. it was alwayw empty
    //searchWords.shift();
  }
  //check if found_words equals to totalWords and found_words is greater than 0
  //so we found as many words as we was searching for inside text
  //and we disallow to return true when found_words = 0
  //(more specifically when totalWords AND found_words are not zero)
  return (found_words == totalWords && found_words > 0) ? true : false;
}

let stringText = 'the lazy fox jumps over the lazy dog';
let searchMe = ['lazy', 'fox', 'dog'];
console.log(multiSearchAnd(stringText, searchMe));
//returns true
//all words was found

stringText = 'the lazy fox jumps over the lazy dog';
searchMe = ['lazy', 'fox', 'cat'];
console.log(multiSearchAnd(stringText, searchMe));
//returns false
//word 'cat' was not found

stringText = 'the lazy fox jumps over the lazy dog';
searchMe = ['dog'];
console.log(multiSearchAnd(stringText, searchMe));
//returns true
//word 'dog' was found

stringText = 'the lazy fox jumps over the lazy dog';
searchMe = ['cat'];
console.log(multiSearchAnd(stringText, searchMe));
// returns false
//word 'cat' was not found

It's case you need the search to be case IN-sensitive then just replace:

if (text.match(new RegExp(searchWords[start], "i"))) {

with:

if (text.match(new RegExp(searchWords[start]))) {

(just remove:

, "i"

)