6
\$\begingroup\$

I am trying to build a small search function where I can search through some JavaScript objects even if there are arrays in it. I got it working but I would like to ask if someone can find a better approach than the one I made. Also, I assume I have some cases I did not think of.

My current search function looks as follows:

function searchFor(needle, haystack, filter, inside){
    if (inside == undefined) inside = 1;
    if (filter){
        for (var c = 0; c < filter.length; c++) {
            var splittedFilter = filter[c].split('.');
            for (var s = 0; s < splittedFilter.length; s++) {
                var obj = haystack[splittedFilter[s]];
                if (obj){
                    if (Array.isArray(obj) && obj.length > 0){
                        for (var i = 0; i < obj.length; i++) {
                            var result = searchFor(needle, obj[i], [splittedFilter[inside]], inside+1);
                            if (result) return true;
                        }
                    } else if( typeof obj == "object"){
                        var result = searchFor(needle, obj, [splittedFilter[inside]], inside+1);
                        if (result) return true;
                    } else {
                        if (obj.toLowerCase().indexOf(needle.toLowerCase()) != -1){
                            return true;
                        }
                    }
                }
            }
        }
    }
    return false
}

The filterlist:

var filter = ['company', 'friends.name'];

The haystack:

var hay =  [
  {
    "id": "57db96f4acfaec3218383063",
    "name": {
      "first": "Nora",
      "last": "Cooke"
    },
    "company": "MITROC",
    "email": "nora.cooke@mitroc.com",
    "friends": [
      {
        "id": 0,
        "name": "West Duke"
      },
      {
        "id": 1,
        "name": "Williams Kelley"
      },
      {
        "id": 2,
        "name": "Amelia Kirk"
      }
    ]
  },
  {
    "id": "57db96f4d5cae409054d3a5b",
    "name": {
      "first": "Dickson",
      "last": "Moses"
    },
    "company": "VISUALIX",
    "email": "dickson.moses@visualix.org",
    "friends": [
      {
        "id": 0,
        "name": "Patty Carr"
      },
      {
        "id": 1,
        "name": "Bowers Wilkerson"
      },
      {
        "id": 2,
        "name": "Fox Kidd"
      }
    ]
  }
];

The execution of the search function:

hay.forEach(function(element) {
    if (searchFor('Duke', element, filter)) {
        document.getElementById('result').innerHTML = document.getElementById('result').innerHTML + '<br />' + JSON.stringify(element);
    }
}, this);
  • needle is the string to search for
  • haystack is one object
  • filter is an array of filters, like: var filter = ['company'];
  • inside is to determine how far in the recursive loop the function is

I made a live example here.

\$\endgroup\$
0
3
\$\begingroup\$

You could use a simplified version for lookup all needed values and the check against the needle.

function search(needle, haystack, filter) {
    return filter.some(function (f) {
        function iter(o, i) {
            var item;

            if (!(ff[i] in o)) {
                return false;
            }

            item = o[ff[i]];
            i++;

            if (i === ff.length) {
                return item.indexOf(needle) !== -1;
            }

            if (Array.isArray(item)) {
                return item.some(function (a) {
                    return iter(a, i);
                });
            }
            return iter(item, i);
        }

        var ff = f.split('.');

        return iter(haystack, 0);
    });
}

var filter = ['company', 'friends.name'],
    hay = [{ "id": "57db96f4acfaec3218383063", "name": { "first": "Nora", "last": "Cooke" }, "company": "MITROC", "email": "nora.cooke@mitroc.com", "friends": [{ "id": 0, "name": "West Duke" }, { "id": 1, "name": "Williams Kelley" }, { "id": 2, "name": "Amelia Kirk" }] }, { "id": "57db96f4d5cae409054d3a5b", "name": { "first": "Dickson", "last": "Moses" }, "company": "VISUALIX", "email": "dickson.moses@visualix.org", "friends": [{ "id": 0, "name": "Patty Carr" }, { "id": 1, "name": "Bowers Wilkerson" }, { "id": 2, "name": "Fox Kidd" }] }, { "id": "57db96f42215bea719d83036", "name": { "first": "Kerr", "last": "Gray" }, "company": "TUBESYS", "email": "kerr.gray@tubesys.io", "friends": [{ "id": 0, "name": "Rose Pierce" }, { "id": 1, "name": "Matthews Moore" }, { "id": 2, "name": "Brenda Ayala" }] }, { "id": "57db96f435d0f064afc90768", "name": { "first": "Elsie", "last": "Dejesus" }, "company": "KLUGGER", "email": "elsie.dejesus@klugger.us", "friends": [{ "id": 0, "name": "Gina Cruz" }, { "id": 1, "name": "Nieves Carrillo" }, { "id": 2, "name": "Chandler Sanford" }] }, { "id": "57db96f4597631fffff36b6b", "name": { "first": "Chan", "last": "Mcdaniel" }, "company": "AQUACINE", "email": "chan.mcdaniel@aquacine.name", "friends": [{ "id": 0, "name": "Hodge Sweeney" }, { "id": 1, "name": "Sweet Burnett" }, { "id": 2, "name": "Lynn Hunter" }] }];

console.log(hay.map(function (a) {
    return search('Sweet', a, filter);
}));

console.log(hay.map(function (a) {
    return search('Rose', a, filter);
}));
.as-console-wrapper { max-height: 100% !important; top: 0; }

\$\endgroup\$
1
  • \$\begingroup\$ This looks exactly like what i want. I am trying to understand this and implement it in my test case and will report back afterwards. \$\endgroup\$ – Styler2go Sep 16 '16 at 9:28
1
\$\begingroup\$

By an invention of Object.prototype.getNestedValue() this job can also be done as follows;

Object.prototype.getNestedValue = function(...a) {
  return a.length > 1 ? (this[a[0]] !== void 0 && this[a[0]].getNestedValue(...a.slice(1))) : this[a[0]];
};

function searchFor(hay,needles,filter){
  var filters = filter.map(f => f.split("."));
  return needles.map(n => hay.map(h => filters.some(f => { var value = h.getNestedValue(f[0]);
                                                           return Array.isArray(value) ? searchFor(value,[n],f.slice(1))[0].reduce((p,c) => p||c)
                                                                                       : value.toLowerCase().indexOf(n) !== -1;
                                                         })));
}

var hay =  [{"id":"57db96f4acfaec3218383063","name":{"first":"Nora","last":"Cooke"},"company":"MITROC","email":"nora.cooke@mitroc.com","friends":[{"id":0,"name":"West Duke"},{"id":1,"name":"Williams Kelley"},{"id":2,"name":"Amelia Kirk"}]},{"id":"57db96f4d5cae409054d3a5b","name":{"first":"Dickson","last":"Moses"},"company":"VISUALIX","email":"dickson.moses@visualix.org","friends":[{"id":0,"name":"Patty Carr"},{"id":1,"name":"Bowers Wilkerson"},{"id":2,"name":"Fox Kidd"}]},{"id":"57db96f42215bea719d83036","name":{"first":"Kerr","last":"Gray"},"company":"TUBESYS","email":"kerr.gray@tubesys.io","friends":[{"id":0,"name":"Rose Pierce"},{"id":1,"name":"Matthews Moore"},{"id":2,"name":"Brenda Ayala"}]},{"id":"57db96f435d0f064afc90768","name":{"first":"Elsie","last":"Dejesus"},"company":"KLUGGER","email":"elsie.dejesus@klugger.us","friends":[{"id":0,"name":"Gina Cruz"},{"id":1,"name":"Nieves Carrillo"},{"id":2,"name":"Chandler Sanford"}]},{"id":"57db96f4597631fffff36b6b","name":{"first":"Chan","last":"Mcdaniel"},"company":"AQUACINE","email":"chan.mcdaniel@aquacine.name","friends":[{"id":0,"name":"Hodge Sweeney"},{"id":1,"name":"Sweet Burnett"},{"id":2,"name":"Lynn Hunter"}]}],
 filter = ['company', 'friends.name'],
needles = ["sweet","rose"],
 result = searchFor(hay,needles,filter);
console.log(result);

\$\endgroup\$
1
  • \$\begingroup\$ If you are going to mutate Object.prototype then consider Object defineProperty. \$\endgroup\$ – Xotic750 Sep 20 '16 at 18:33
1
\$\begingroup\$

For your specific haystack and filter, without recursion, and using ES6.

function customGetValues(object, path) {
  path = path.split('.');
  const last = path.pop();
  return path.length ? object[path].map(v => v[last]) : [object[last]];
}

function searchFor(needle, haystack, filter) {
  return filter.some(f => customGetValues(haystack, f).some(v => v.includes(needle)));
}

const hay = [{
  "id": "57db96f4acfaec3218383063",
  "name": {
    "first": "Nora",
    "last": "Cooke"
  },
  "company": "MITROC",
  "email": "nora.cooke@mitroc.com",
  "friends": [{
    "id": 0,
    "name": "West Duke"
  }, {
    "id": 1,
    "name": "Williams Kelley"
  }, {
    "id": 2,
    "name": "Amelia Kirk"
  }]
}, {
  "id": "57db96f4d5cae409054d3a5b",
  "name": {
    "first": "Dickson",
    "last": "Moses"
  },
  "company": "VISUALIX",
  "email": "dickson.moses@visualix.org",
  "friends": [{
    "id": 0,
    "name": "Patty Carr"
  }, {
    "id": 1,
    "name": "Bowers Wilkerson"
  }, {
    "id": 2,
    "name": "Fox Kidd"
  }]
}, {
  "id": "57db96f42215bea719d83036",
  "name": {
    "first": "Kerr",
    "last": "Gray"
  },
  "company": "TUBESYS",
  "email": "kerr.gray@tubesys.io",
  "friends": [{
    "id": 0,
    "name": "Rose Pierce"
  }, {
    "id": 1,
    "name": "Matthews Moore"
  }, {
    "id": 2,
    "name": "Brenda Ayala"
  }]
}, {
  "id": "57db96f435d0f064afc90768",
  "name": {
    "first": "Elsie",
    "last": "Dejesus"
  },
  "company": "KLUGGER",
  "email": "elsie.dejesus@klugger.us",
  "friends": [{
    "id": 0,
    "name": "Gina Cruz"
  }, {
    "id": 1,
    "name": "Nieves Carrillo"
  }, {
    "id": 2,
    "name": "Chandler Sanford"
  }]
}, {
  "id": "57db96f4597631fffff36b6b",
  "name": {
    "first": "Chan",
    "last": "Mcdaniel"
  },
  "company": "AQUACINE",
  "email": "chan.mcdaniel@aquacine.name",
  "friends": [{
    "id": 0,
    "name": "Hodge Sweeney"
  }, {
    "id": 1,
    "name": "Sweet Burnett"
  }, {
    "id": 2,
    "name": "Lynn Hunter"
  }]
}];

const filter = ['company', 'friends.name'];
console.log('Sweet', hay.map(element => searchFor('Sweet', element, filter)));
console.log('Rose', hay.map(element => searchFor('Rose', element, filter)));

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.