HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptMinor

My attempt at a weighted search in JavaScript

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
javascriptweightedsearchattempt

Problem

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 > 0;
        }).sort(function (obj1, obj2) {
            //sort first by weight
            if (obj1.weight > obj2.weight) {
                return -1;
            } else if (obj1.weight  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) >= 0; }, weight: 2 },
        { test: function (testElement) { return testElement.description.toLowerCase().indexOf(search) >= 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

Solution

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.

Code Snippets

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)
     };
 })
//sort first by weight if possible
var weightDifference = obj2.weight- obj1.weight;
if (weightDifference) {
  return weightDifference;
}

Context

StackExchange Code Review Q#43500, answer score: 4

Revisions (0)

No revisions yet.