0

Given a JS as follows:

for (c in chars) {
    for (i in data) {
        if (data[i].item === chars[c]) {
            // do my stuff;
        }
        else { /* do something else */}
    }
}

and data such:

var chars = [ 'A', 'B', 'C', 'A', 'C' ];
var data = [
    {'item':'A', 'rank': '1'}, 
    {'item':'B', 'rank': '2'}, 
    {'item':'C', 'rank': '3'}
    // no duplicate
];

Is there a simpler syntax to express that rather than nested for loops and inner conditions?

I try to match two datasets, more precisely to use chars's keys to iterate data and find values.

Eliran Malka
  • 15,821
  • 6
  • 77
  • 100
Hugolpz
  • 17,296
  • 26
  • 100
  • 187
  • 3
    Wait, are you trying to [iterate over an array with `for in`](http://stackoverflow.com/a/17499001/1850609)? – acdcjunior Jul 20 '13 at 22:34
  • @acdcjunior It works. Just remember that the iteration variable is set to the indexes, not the values (it's not like PHP `foreach`). – Barmar Jul 20 '13 at 22:35
  • I try to match two datasets, more precisely to use `chars`'s keys to iterate `data` and find values. – Hugolpz Jul 20 '13 at 22:36
  • @Hugolpz you can use something like this: `data.forEach(function(i){if(c.indexOf(i.item)!=-1){}else{}});` – Mehdi Yeganeh Jul 20 '13 at 22:37
  • 2
    @Barmar What works? The `for in` for arrays? If so, I'm not saying it doesn't, I'm say one shouldn't, as it can screw your code up. – acdcjunior Jul 20 '13 at 22:37
  • @Hugolpz what's the intended platform? What browsers? Just the newest? – acdcjunior Jul 20 '13 at 22:38
  • With the example data there only needs to be one loop as c and I are the same value when their values match. The question needs a representative example. – AD7six Jul 20 '13 at 22:41
  • Will there ever be duplicate chars? Or dupicate data items? Or both? If so, should there be multiple positives for the dupes? –  Jul 20 '13 at 22:42
  • Are you forced to have your data in that format, or can you use an map with the obvious key? – Dave Newton Jul 20 '13 at 22:45
  • So you just need a map of letters to rank. Why not just do a map as suggested by @DaveNewton? –  Jul 20 '13 at 22:51
  • acdc: for a web app. Crazy: for duplicata, I clarified in my question. @AD7: #Fiddle : http://jsfiddle.net/bysBH/4/ (the question stays to make a simplier JS) – Hugolpz Jul 20 '13 at 22:52
  • @Hugolpz Web app, ok, but, what browsers do you wish to support? The latest have many possibly useful array functions. But if you wish to support olders, you'd have less options. – acdcjunior Jul 20 '13 at 22:53
  • Chrome and modern browsers. I don't bother with dinos. – Hugolpz Jul 20 '13 at 22:55
  • But the `chars` represents the `textarea` input, right? So it doesn't matter if there's duplictes. An `"A"` will always look up `"A"` in the map, and get `"easy"`. http://jsfiddle.net/bysBH/5/ –  Jul 20 '13 at 22:57
  • 1
    ...or like this: http://jsfiddle.net/bysBH/7/ –  Jul 20 '13 at 23:00
  • Check [**this fiddle**](http://jsfiddle.net/acdcjunior/bysBH/8/) out. It uses [`Array.reduce()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FArray%2FReduce) ([IE9 and later](http://kangax.github.io/es5-compat-table/#Array.prototype.reduce)) and converts your `da` object into a `da2` map for quicker acessing. – acdcjunior Jul 20 '13 at 23:12
  • @acdcjunior: wow, a bit high level for me (beginner). – Hugolpz Jul 21 '13 at 18:33
  • @CrazyTrain's fiddle nice too, and easier for me ! – Hugolpz Jul 21 '13 at 18:34
  • @CrazyTrain: While it doesn't really answer to the nested loops simplification, I will use your witty code. – Hugolpz Jul 21 '13 at 19:27
  • 1
    @Hugolpz: Yeah, I think your nested loops are about as simple as they can get. In this specific case, a key/value map is definitely the way to go. Will be faster too. –  Jul 21 '13 at 19:31

3 Answers3

1

You could do this:

for (i = 0; i < data.length; i++) {
    if (chars.indexOf(data[i].item) != -1) {
        // Do something
    } else {
        // Do something else
    }
}

However, if chars is large, I would create an object whose keys are the elements of chars and use if (chars_obj[data[i].item]). This is more efficient than searching an array every time.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • 2
    with a sidenote: `Array.indexOf` isn't supported in older browsers. – adeneo Jul 20 '13 at 22:39
  • I don't know what OP specifically needs, but this will be a little different if there's more than one of the same letter in the `chars` Array. –  Jul 20 '13 at 22:43
  • @Barmar this is not what the OP wants. With the OP example, 15 functions are executed (either my stuff or something else). – Christophe Jul 20 '13 at 23:04
1

An alternative to simplifying the code would be to encapsulate and abstract it away into a utility that accepts callbacks, than reuse that whenever needed, like so:

// definition
function eachChar(onMatch, onMismatch) {
    for (c in chars) {
        for (i in data) {
            if (data[i].item === chars[c]) {
                typeof onMatch === 'function' && onMatch(); 
            } else {
                typeof onMismatch === 'function' && onMismatch(); 
            }
        }
    }
}

// usage examples
eachChar(function() {
    // do something when it's a match
});
eachChar(function() {
    // do something when it's a match
}, function() {
    // do something else when it's not
});

See a live demo on jsFiddle.


As a sidenote, you would want to explicitly declare variables used as loop indexes, as to not exposing them in an outer scope (e.g. the global scope):

// that:
for (c in chars) {
    for (i in data) {

// would become this:
for (var c in chars) {
    for (var i in data) {
Eliran Malka
  • 15,821
  • 6
  • 77
  • 100
0

Since you tagged your question jquery, you could use a jquery solution:

$.each(chars, function (cndx, chr) {
    $.each(data, function (dndx, datum) {
        if (datum.item === chr) {
            // do my stuff;
        } else {
            /* do something else */
        }
    }
});

Not any more succinct, but at least you don't have to index.

Eliran Malka
  • 15,821
  • 6
  • 77
  • 100
Chip Camden
  • 210
  • 1
  • 7