2

I have a ajax request that is returning to me an object that looks roughly like the following:

[
 {category: "Test Category", id: "1", name: "Test",  language: "English"},
 {category: "Test Category", id: "2", name: "Test2", language: "English"},
 {category: "A test",        id: "3", name: "Test3", language: "Spanish"},
 {category: "Test Category", id: "4", name: "Test4", language: "Spanish"}
]

Basically I need a way to do the following:

  • List all categories that exist in ONLY English or Spanish
  • List all ID's that exist in a specified language
  • List all ID's that exist in a specified category

Now I'm fairly sure the best route to do this is to create my own temp arrays that can house that data but I'm not confident enough in Javascript to write the correct loop to get that data. I know in PHP I'd do something like $category['Test Category'][] = 3 while looping.

The idea is I need the ability to filter things based on the user selecting to filter by English and then by a category within that.

Can someone point me in the right direction?

4
  • 2
    That sounds like a lot of work. Instead, use underscore.js - underscorejs.org. You can use filter, pluck, map, and so forth. Commented Dec 20, 2012 at 21:48
  • You can do something like you do in PHP, except $arr[] = $x is arr.push(x) Commented Dec 20, 2012 at 21:49
  • don't forget arr = arr || [] which creates a new array if it doesn't exist. Commented Dec 20, 2012 at 21:50
  • 2
    Or if you use jquery, use jquery grep api.jquery.com/jQuery.grep Commented Dec 20, 2012 at 21:51

5 Answers 5

3

Some ways to use underscore for your tasks: http://underscorejs.org

var data = [
 {category: "Test Category", id: "1", name: "Test",  language: "English"},
 {category: "Test Category", id: "2", name: "Test2", language: "English"},
 {category: "A test",        id: "3", name: "Test3", language: "Spanish"},
 {category: "Test Category", id: "4", name: "Test4", language: "Spanish"}
]

// Filter by spanish
var output = _.where(data, {"language":"Spanish"});

// Filter by category
var output = _.where(data, {"category":"A test"});

// List specific category ids
var output = _.pluck(_.where(data, {"category":"A test"}), "id");

// List category names
var output = _.pluck(_.where(data, {"language":"English"}), "category");

// Group by languages
var output = _.groupBy(data, function(value) { 
  return value.language
});
output["English"];
Sign up to request clarification or add additional context in comments.

4 Comments

Who, are you; a dating agent? Because you're making me fall in love with underscore.js ;-)
No no I agree with Jan. I may use this :)
What's the point of _.invoke? It looks like a _.map with eval.
More like an '_.each' with 'eval'. Underscore is all about convenience and brevity, and one of the things you learn early on is that you can do almost everything with _.each, but shouldn't.
2

Since you included jQuery in the tag I thought I would use it:

//All categories with English or spanish language (categories is the array)
$.grep(categories, function(c) { return c.language=='English' || c.language == 'Spanish'; });

Comments

1

Using vanilla JS, where json is your original array, you could use:

function filterJson(attr, obj) {
    var attrList = [],
        match,
        tempAttr,
        i;

    for (i = 0; i < json.length; i += 1) {
        match = true;

        for (tempAttr in obj) {
            if (!obj[tempAttr].test(json[i][tempAttr])) {
                match = false;
                break;
            }
        }

        if (match) attrList.push(json[i][attr]);
    }

    return attrList;
}

And use it like this:

"List all categories that exist in ONLY English or Spanish"

filterJson('category', {
    language: /(English|Spanish)/
});
// => ["Test Category", "Test Category", "A test", "Test Category"] 

"List all ID's that exist in a specified language"

filterJson('id', {
    language: /English/
});
// => ["1", "2"] 

"List all ID's that exist in a specified category"

filterJson('id', {
    category: /Test Category/
});  
// => ["1", "2", "4"] 

Comments

0

If you can count on a few assumptions about the data always being true -- eg, that you'll only ever have category:, id:, name:, and language:, and that there will never be a colon or comma anywhere in the name or category -- then I would actually suggest sticking all the information into single strings and use RegExp to find it.

Eg:

[
 "category:Test Category, id:1, name:Test,  language:English",
 "category:Test Category, id:2, name:Test2, language:English",
 "category:A test,        id:3, name:Test3, language:Spanish",
 "category:Test Category, id:4, name:Test4, language:Spanish"
]

Now, to find a values for an entry, use RegExp.match().

var m = /^category:([^,]+)\s+id:(\d+),\s+name:([^,]+),\s+language:(.+)$/i;
// m[1] = Test Category
// m[2] = 1;
// m[3] = Test2
// m[4] = Spanish

And you can search by using tests.

function my_filter(criteria) {
    var results = [];
    var re = new RegExp(criteria);
    for (var i=0; i<array.length; i++) {
        if (array[i].test(re))
            results.push(array[i]);
    }
    return results;
}
var english_array = my_filter('language:english');
var atest_array = my_filter('category:a test');

2 Comments

Can you explain your logic for suggesting this solution? It's different and I'm curious about the benefits.
Regex parsing is powerful, and more importantly... native in the browser, so it's likely to be much faster than a complex javascript parsing. If you need to, you can match easily on multiple criteria just by altering the regular expression. Whenever I work with relatively simple data, I find it much easier and faster to work natively with strings.
-1

The magic Google terms are "map" and "reduce".

var data = [...the JSON data parsed with JSON.parse() if necessary...]

var categoriesInEnglish = data.reduce(function(accumulator, category) {
    if(category.language = 'English') {
        accumulator.push(category.id);
    }
});

see http://www.mrspeaker.net/2011/05/02/conventional-interfaces/ and http://www.mrspeaker.net/2011/04/27/reducing-map/

3 Comments

What's the accumulator on the first call? I doubt it's an empty array.
It's the array of answers. If you're going for ultimate compatability, jQuery's $.map() and $.grep() are probably simpler implementations.
This won't work. When the callback is first called, its first argument is the first element of the array. Additionally, on each subsequent call, the accumulator is undefined, throwing a TypeError.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.