14

I have a JavaScript object like this:

[{
    name : "soccer",
    elems : [
        {name : "FC Barcelona"},
        {name : "Liverpool FC"}
    ]
},
{
    name : "basketball",
    elems : [
        {name : "Dallas Mavericks"}
    ]
}]

Now I want to search on this JavaScript object in the browser. The search for "FC" should give me something like this:

[
    {name : "FC Barcelona"},
    {name : "Liverpool FC"}
]

How to do this fast? Are there any JavaScript libs for this?

Crescent Fresh
  • 115,249
  • 25
  • 154
  • 140
Juri Glass
  • 88,173
  • 8
  • 33
  • 46

6 Answers6

14

You might like using jLinq (personal project)

http://hugoware.net:4000/Projects/jLinq

Works like LINQ but for JSON and it allows you to extend it and modify it however you want to. There is already a bunch of prebuilt methods to check values and ranges.

rene
  • 41,474
  • 78
  • 114
  • 152
hugoware
  • 35,731
  • 24
  • 60
  • 70
10

Seeing as though the only helpful answers have been referencing third party libraries - here's your native javascript solution. For anyone that only wants a few lines of code rather than a stack:

The function:

Array.prototype.findValue = function(name, value){
   var array = map(this, function(v,i){
        var haystack = v[name];
        var needle = new RegExp(value);
        // check for string in haystack
        // return the matched item if true, or null otherwise
      return needle.test(haystack) ? v : null;
   });
  return array;
}

A native .map() function:

map = function(array, mapFunction) {
      var newArray = new Array(array.length);
      for(var i = 0; i < array.length; i++) {
        newArray[i] = mapFunction(array[i]);
      }
      return newArray;
}

Your object:

(skimmed from your posted abject):

myObject = {
        name : "soccer",
        elems : [
            {name : "FC Barcelona"},
            {name : "Liverpool FC"}
        ]
    },
    {
        name : "basketball",
        elems : [
            {name : "Dallas Mavericks"}
        ]
    }

For usage:

(This will search your myObject.elems array for a 'name' matching 'FC')

var matched = myObject.elems.findValue('name', 'FC');
console.log(matched);

The result - check your console:

[Object, Object, findValue: function]
0: Object
name: "FC Barcelona"
__proto__: Object
1: Object
name: "Liverpool FC"
__proto__: Object
length: 2
__proto__: Array[0]
J Charles
  • 390
  • 3
  • 7
4

Try jOrder. http://github.com/danstocker/jorder

It's optimized for fast O(logn) search and sorting on large (thousands of rows) tables in JS.

As opposed to array iteration, which most of the answers here are based on, jOrder uses indexes to filter data. Just to give you an idea, free-text search on a 1000-row table completes about 100 times faster than iteration. The bigger the table, the better ratio you get.

However jOrder can't process the format of your sample data. But if you re-format it like this:

var teams =
[
{ sport : "soccer", team: "FC Barcelona" },
{ sport : "soccer", team: "Liverpool FC" },
{ sport : "basketball", team : "Dallas Mavericks"}
]

You can get the desired results by first setting up a jOrder table:

var table = jOrder(teams)
    .index('teams', ['team'], { grouped: true, ordered: true, type: jOrder.text });

And then running a search on it:

var hits = table.where([{ team: 'FC' }], { mode: jOrder.startof });

And you'll get exactly the two rows you needed. That's it.

Dan Stocker
  • 702
  • 7
  • 5
1

The straightforward way to do this is simply to iterate over every property of the object and apply a test function to them (in this case, value.contains("FC")).

If you want it to go faster, you'd either need to implement some kind of caching (which could be eagerly populated in the background ahead of any queries), or perhaps precalculate the result of various popular test functions.

Andrzej Doyle
  • 102,507
  • 33
  • 189
  • 228
  • I would like to go faster than O(n). I thought about something like http://en.wikipedia.org/wiki/Trie , but I don't want to write this on my own if it's not necessary. – Juri Glass Nov 30 '09 at 15:56
  • @Juri, are you willing to preprocess the object into a different structure, or do you want to use the object as-is? There is a cost, of course, to converting from one structure to another. It would be worth it, possibly, if you were going to do a lot of searches on the same data. – Nosredna Nov 30 '09 at 16:01
  • @Nosredna: Yes, preprocessing is absolutely possible. – Juri Glass Nov 30 '09 at 16:19
  • 1
    @Juri - By definition you can only go faster than O(n) if an object contains some information about the other objects; that is, given the output of running your test function on one object you can deduce the output for at least some of the others. Besides, **why** do you want faster than O(n)? Do you know that you need good asymptotic performance with a massive number of objects? I suspect that you actually don't. – Andrzej Doyle Nov 30 '09 at 16:20
0

You could do this with regular expressions performed against a serialized JSON string:

var jsonString = "[{ name : \"soccer\", elems : [ {name : \"FC Barcelona\"}"
    +", {name : \"Liverpool FC\"}]},{name : \"basketball\",elems : ["
    +"{name : \"Dallas Mavericks\"} ]}]";

var pattern = /\s*([\w\d_]+)\s*:\s*((\"[^\"]*(your pattern here)[^\"]*\")|(\'[^\']*(your pattern here)[^\']*\'))\s*/g;

var foundItems = [];
var match;
while(match = pattern.exec(jsonString)){
  foundItems.push(match[0]);
}

var foundJSON = "[{" + foundItems.join("}, {") + "}]";
var foundArray = eval(foundJSON);

I haven't tested the loop part of this, but the Regex seems to be working well for me with simple tests in firebug.

Crescent Fresh
  • 115,249
  • 25
  • 154
  • 140
JasonWyatt
  • 5,275
  • 1
  • 31
  • 39
-3

In regards to AngularJS, you can do this:

var item = "scope-ng-model";

(angular.element('form[name="myForm"]').scope())[item] = newVal;
bummi
  • 27,123
  • 14
  • 62
  • 101
Shany
  • 1