tags:

views:

198

answers:

6

Hi

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?

+2  A: 

Check this out:

http://goessner.net/articles/JsonPath/

Lior Cohen
+1  A: 

Although you could do it in straight JavaScript, underscore.js is a good library to handle this stuff. You could probably get it going with "pluck" and "each."

Nice thing about underscore.js is that it uses the browser's built-in calls when they exist.

Nosredna
A: 

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
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
@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
@Nosredna: Yes, preprocessing is absolutely possible.
Juri Glass
@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
How big do these lists get?
Nosredna
+1  A: 

You might like using jLinq (personal project)

http://Hugoware.net/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.

Hugoware
That's a great project.
Nosredna
That's a great project, and a beautiful web page for it. I did see a type you might want to correct... "Read The Documenation"
Nosredna
@Nosredna - Thank you, I'll check into it!
Hugoware
+1  A: 

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.

JasonWyatt
lol, thanks for the fix, Crescent :)
JasonWyatt
A: 

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