3

Consider this very simple code:

someLabel:
for (var i=0; i<newFonts.length; i++) {
    var newFont = newFonts[i];

    for (var j=0; j<oldFonts.length; j++) {
        var oldFont = oldFonts[j];

        if (fb.equals(oldFont, newFont)) {
            // Break out of inner loop, don't finish outer iteration, continue with next outer item
            continue 2;
        }
    }

    // This must only happen if `newFont` doesn't match ANY `oldFonts`
    oldFonts.push(newFont);
}

What it's supposed to do is compare all oldFonts objects with newFonts objects and only add newFont to oldFonts (oldFonts.push) if it doesn't already exist (fb.equals).

oldFonts and newFonts are objects with name and host properties. Both are used in fb.equals() to determine equality. indexOf() won't work.

This is exactly how I'd do it in PHP. It doesn't work in JS, because JS doesn't support continue 2, which means continue 2 levels.

How do I do this in JS??

  • continue wont do, because it'll still finish the inner loop and end up at the push
  • break won't do, because it'll skip the inner loop and jump straight to the push
  • break someLabel won't do, because I don't want to skip ALL newFonts when ONE must be ignored

This must be possible without a single function...

Rudie
  • 52,220
  • 42
  • 131
  • 173

2 Answers2

2

The comments demonstrated how to continue to a labeled statement. But as to your original problem, you might be able to solve it more easily:

var difference = function(x, y) {
    return x.filter(function(e) {return y.indexOf(e) < 0;});
};

// or oldFonts = ... if you prefer to mutate
var combinedFonts = oldFonts.concat(difference(newFonts, oldFonts));

Update

If the requirement for testing equality is more sophisticated, as noted in the comments, then something slightly more is needed. To my mind, this is still simpler than the original approach with labeled loops:

var complement = function(pred, a, b) {
    return a.filter(function(x) {
        return !b.some(function(y) {return pred(x, y);});
    });
};

var sameName = function(a, b) {return a.name === b.name;};

var combinedFonts = oldFonts.concat(complement(sameName, newFonts, oldFonts));

You can see this on JSFiddle


I can't wait for fat-arrows to be widely available. Here's the equivalent:

var complement = (pred, a, b) => a.filter(x => !b.some(y => pred(x, y)));
var sameName = (a, b) => a.name === b.name;
Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103
  • `oldFonts` and `newFonts` aren't `Array`, they're `Array` with `Font.name` being the font, so `indexOf()` won't work. I see now that's wasn't obvious, at all. Updated question. – Rudie Jul 15 '15 at 16:49
  • Updated the answer to deal with a more sophisticated matching mechanism. – Scott Sauyet Jul 16 '15 at 13:40
0

perhaps something like this would help solve your problem:

var oldfonts = ['courier new', 'comic sans', 'century gothic'];
var newfonts = ['times new roman', 'comic sans', 'wingdings'];
var addfonts = [];
for (var i = 0; i < newfonts.length; i++) {
    if (oldfonts.indexOf(newfonts[i]) < 0) { // if newfonts item doesnt exist in oldfonts array
        addfonts.push(newfonts[i]); // add it to addfonts array to be added to oldfonts array systematically after all different font items are found           
    }
}
for(var i = 0; i < addfonts.length; i++) {
    oldfonts.push(addfonts[i]); // push add fonts to oldfonts array
    console.log(oldfonts);
}
indubitablee
  • 8,136
  • 2
  • 25
  • 49
  • `oldFonts` and `newFonts` aren't `Array`, they're `Array` with `Font.name` being the font, so `indexOf()` won't work. I see now that's wasn't obvious, at all. Updated question. – Rudie Jul 15 '15 at 16:46