My attempt at a weighted search in JavaScript I tried to make a weighted search function in JavaScript. I've been improving my JS lately but still not sure about some best practices, wondering if there's any improvements I could make, or if I've ended up doing it completely wrong.

The intention is to keep it quite flexible so it can take an array of filter functions, each with its own weight, it also takes a property on which to sort items with equal weights.

The search function is

function weightedSearch(array, weightedTests, sortProperty) {
        return array.map(function (e) {
            return {
                element: e, weight: weightedTests.map(function (weightedTest) {
                    var testResult = weightedTest.test(e);
                    return testResult * weightedTest.weight;
                }).reduce(function (previousValue, currentValue) { return previousValue + currentValue; }, 0)
            };
        }).filter(function (element) {
            return element.weight &gt; 0;
        }).sort(function (obj1, obj2) {
            //sort first by weight
            if (obj1.weight &gt; obj2.weight) {
                return -1;
            } else if (obj1.weight &lt; obj2.weight) {
                return 1;
            }

            // Else by chosen property
            if (obj1.element[sortProperty] &lt; obj2.element[sortProperty]) {
                return -1;
            } else if (obj1.element[sortProperty] &gt; obj2.element[sortProperty]) {
                return 1;
            } else {
                return 0;
            }
        }).map(function (e) {
            return e.element;
        });
    }


A weighted test is an object of the form:

{test: function(element){}, weight: relativeWeightOfTheTest}


And would be used in a manner such as:

var search = "test";
var results = weightedSearch(arrayToSearch, [
        { test: function (testElement) { return testElement.title.toLowerCase().indexOf(search) &gt;= 0; }, weight: 2 },
        { test: function (testElement) { return testElement.description.toLowerCase().indexOf(search) &gt;= 0; }, weight: 0.5 }
    ], "title")


This would return an array where either the title or description contain "test" and ordered by:


Appearing in title and description
Appearing in title only
Appearing in description only


Any results with the same weighting are then ordered by their title. Obviously this is a simple test case, you could easily have a test that adds up instances of the search string, the test could check it against a list of synonyms before counting etc.

Here's a JSbin with some test data and a quick Knockout usecase.

http://jsbin.com/figay/1/edit?html,js,output
I like it, from a once over:


map, filter, reduce etc. are not the most efficient functions, they make for readable code, not for speedy sorting/searching. See this : http://jsperf.com/arraymap, a simply for loop beats everything else every time, this also goes for filter,reduce etc.
I would change around the first block, there is too much happening horizontally:

return array.map(function (e) {
    return {
        element: e, 
        weight: weightedTests.map(function (weightedTest) {
            return weightedTest.test(e) * weightedTest.weight;
        }).reduce(function (previousValue, currentValue) { 
            return previousValue + currentValue; }, 0)
     };
 })

The sorting by weight I would do like this:

//sort first by weight if possible
var weightDifference = obj2.weight- obj1.weight;
if (weightDifference) {
  return weightDifference;
}

The last return 0 does not need to be in an else block
Other than that the code runs fine on JsHint, is well commented and easy to follow.