1

Considering a function query_item that either returns an object to be processed or null if nothing is to be processed anymore.

let item = query_item();
while(item !== null){
    process(item);
    item = query_item();
}

Clearly, this is a small violation of DRY (Don't repeat yourself), mostly mitigated by having query_item() as a dedicated function.

I am aware that in this simple case, clarity is much more important than preventing repetition.

Nevertheless, is there a way to write a loop like this without repeating the assignment?

The only thing that I came up with was a for-loop approach, but that has the same repetition and is - in my eyes at least - a little harder to read.

for(let item = query_item();
    item !== null;
    item = query_item()
){
    process(item);
};
iFreilicht
  • 13,271
  • 9
  • 43
  • 74
  • 1
    [This answer](http://stackoverflow.com/questions/151850/why-would-you-use-an-assignment-in-a-condition/151870#151870) might help you out. – Jo. May 15 '17 at 18:18

5 Answers5

3

This is the standard scenario where one would use an assignment inside the while condition:

let item;
while (item = query_item()) {
    process(item);
}

or possibly, if you want to be explicit:

let item;
while ((item = query_item()) !== null) {
    process(item);
}

You also can use a for loop with the same approach, which also gives you block scope for let:

for (let item; item = query_item(); ) {
    process(item);
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Have you tested that assignment? – Matheus Valiente Souza May 15 '17 at 18:20
  • 1
    @MatheusValienteSouza he has 250k rep, he doesnt need to, he *knows* – Jonas Wilms May 15 '17 at 18:21
  • Ah right, I tried it with the declaration inside and that didn't work, but just the assignment seems to be perfect. @MatheusValienteSouza I don't see how that assignment couldn't work. Am I missing something? – iFreilicht May 15 '17 at 18:24
  • @iFreilicht i still think that a declaration should be an expression that returns the assigned value, but thats how it is... – Jonas Wilms May 15 '17 at 18:25
  • I had the same idea from @Bergi(the assignment whith the !==null condition), but it has not assigned the value correctly. I'll try it again. – Matheus Valiente Souza May 15 '17 at 18:30
  • 1
    Just pointing out - for the language learners - that the first `while` and last `for` loop additionally terminate on encountering `undefined`, `""`, `0`, `false` and `NaN`. – le_m May 15 '17 at 19:13
0

Little prettier?

let item;
do {
  item = query_item();
  if (item) process(item);
} while (item !== null)
Michał Sałaciński
  • 2,256
  • 1
  • 11
  • 10
  • I'd recommend `else break; } while (true)` to avoid repeating the test – Bergi May 15 '17 at 18:19
  • 1
    That's doing the test twice, though. And when just using `if (item)`, you now have two different conditions for executing `process` and breaking the loop. @Bergi has a good solution for that, but it's hardly prettier than his answer. – iFreilicht May 15 '17 at 18:22
0

If you want to completely avoid this assignment weirdness and get the plainest possible loop syntax, you might consider using an ES6 iterator:

function query_items() {
    return {
        [Symbol.iterator]() {
            return this;
        },
        next() {
            return {
                value: query_item(),
                get done() { return this.value === null }
            };
        }
    };
}

for (let item of query_items()) {
    process(item);
}

Or even better implement query_items as a generator function right away.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
0

An alternative to assignment within the loop condition...

let item;
while (item = query_item()) {
    process(item);
}

...is a generator function which terminates upon encountering a null value:

function* iterate(cb) {
  let next = cb();
  if (next === null) return;
  yield next, yield* iterate(cb);
}

for (let item of iterate(query_item)) {
  process(item);
}
Community
  • 1
  • 1
le_m
  • 19,302
  • 9
  • 64
  • 74
-1

For javascript I believe you could write:

while(let item = query_item() && item){
    process(item);
}