12

You can use destructuring assignment to define enumerations in ES6 as follows:

var [red, green, blue] = [0, 1, 2];

Instead, I'd like the right hand side of the destructuring assignment to be dynamic. For example:

var MAX_ENUM_SIZE = 32;
var ENUM = new Array(MAX_ENUM_SIZE);
for (var i = 0; i < MAX_ENUM_SIZE; i++) ENUM[i] = i;

var [red, green, blue] = ENUM;

Unfortunately, this seems like a hack. What if I want a bigger enumeration in the future? Hence, I was thinking of using destructuring assignment with an iterator as follows:

var [red, green, blue] = enumeration(/* I don't want to specify size */);

However, I don't think it's possible to use destructuring assignment with iterators[citation needed]. Is there any way to accomplish this goal?

Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299

2 Answers2

13

Use a generator

function* enumerator() {
  let i = 0;
  while (true) yield i++;
};

let [red,green,blue] = enumerator();
console.log(red, green, blue); // 0 1 2

let [a,b,c,d,e] = enumerator();
console.log(a,b,c,d,e); // 0 1 2 3 4

The generator is flexible making this pretty neat for implementing different types of enums – for example, these cute bitmask enums

function* bitmask() {
  let i = 0;
  while (i < 32) yield 1 << i++;
  throw Error("bitmask enumerator exceeds 32 bits");
}

let [R,W,X] = bitmask();

const read = p => (p & R) !== 0;
const write = p => (p & W) !== 0;
const exec = p => (p & X) !== 0;

{
  let p = R | W; // read and write only
  console.log("can read?", read(p));   // true
  console.log("can write?", write(p)); // true
  console.log("can exec?", exec(p));   // false
}

{
  let p = R | X; // read and execute only
  console.log("can read?", read(p));    // true
  console.log("can write?", write(p));  // false
  console.log("can exec?", exec(p));    // true
}
Mulan
  • 129,518
  • 31
  • 228
  • 259
  • 1
    To be honest, I didn't know it was possible to destructure a generator before trying this. Pretty cool trick – of course, when used responsibly. I think your enumerator case is a pretty good fit for this. – Mulan Sep 24 '16 at 07:28
  • That bitmask generator should loop from `0` to `30` (or `31` at most) and then throw an exception. – Bergi Sep 25 '16 at 20:46
  • Coming from Python, where the generator is required to have exactly as many items as the number of variables, reading code like this is going to feel kind of weird. – user2357112 Sep 26 '16 at 01:45
  • 1
    @Bergi thanks – it was presented as a naïve implementation but it's better to be thorough ^_^ – Mulan Sep 26 '16 at 03:31
  • Zero evaluates to a falsey value. You may want to start your enumerations with i=1. – Bikibird Feb 10 '19 at 19:37
-3

Note, each of the below prospective approaches could probably be improved.

The variables appear to be global at Question. You can create an array of strings referencing the variable which should be created, define the variable from the element of the array

// not technically destructuring, though achieves same result;
// that is, setting variables globally
for (var prop of (props = ["a", "b", "c", "d"])) {
  // set identifier globally
  self[prop] = props.indexOf(prop); // set a value for each `prop`
}
// delete `prop`, `props`
prop = props = void 0;

console.log(a, b, c, d);

Other approaches

using object destructuring

var {
  red, blue, green
} = (function(data, props) {
      for (var prop of Object.keys(props)) {
        data[props[prop]] = props.indexOf(props[prop]); // or some other value
      };
      return data
    }({}, ["red", "blue", "green"]));

console.log(red, blue, green);

using a list of variables to be

var props = ["red", "blue", "green"]; // list of variables to be
var [red, blue, green] = props.map((v, k) => k);
console.log(red, blue, green, "window[\"red\"]:", window[props[0]]);
guest271314
  • 1
  • 15
  • 104
  • 177
  • 1
    I appreciate the effort that you put into this answer but I think you misinterpreted my question. I upvoted your answer anyway. =) – Aadit M Shah Sep 24 '16 at 07:31
  • 2
    Note `Array.from` takes a second argument which is a mapping function – aside from that, I'm not really sure what this answer is "solving" – Mulan Sep 24 '16 at 07:32
  • @naomik Adjusted approach to use `Array.from()` without passing variables – guest271314 Sep 25 '16 at 19:44
  • @AaditMShah If it doesn't match the question, why upvote? It should be a comment, not an answer. – Bergi Sep 25 '16 at 20:47
  • @Bergi _"If it doesn't match the question"_ Please explain how `javascript` at Answer does not match Question? – guest271314 Sep 25 '16 at 20:55
  • 1
    OP asked for a solution that does not involve specifying a (maximum) size. Your answer fails to provide that. – Bergi Sep 25 '16 at 20:57
  • @Bergi Have you read original Question? Approach at Answer uses same pattern OP uses at original Question. Technically `num` could be the maximum number of elements an `Array` can contain. Or, pass the identifiers to the function on right side to return exact number of identifiers passed on left side. – guest271314 Sep 25 '16 at 20:58
  • @guest271314 I have. OP knows that approach, he considers it hacky, and ask for one that does not involve specifying a size. You might want to re-read the question. – Bergi Sep 25 '16 at 21:00
  • @Bergi As described at previous comment, pass the maximum number of elements an `Array` can contain, or the exact number of identifiers defined on left side, as previous approach at Answer achieved `var [red, blue, green] = [...Array.from(Array(red, blue, green), (_, i) => i)]; console.log(`red:${red}, blue:${blue}, ${green}`);` – guest271314 Sep 25 '16 at 21:01
  • @Bergi Perhaps that is Answer will stick with. See updated post. – guest271314 Sep 25 '16 at 21:06
  • Duplicating the identifiers is not DRY and throws exceptions with `let`/`const`. And passing the maximum number of array elements is a memory-heavy hack that deserves to be downvoted… – Bergi Sep 25 '16 at 21:08
  • @Bergi Well, for now, current approach is what am staying with. Will try to adjust to provide for the issues you mentioned. Thanks – guest271314 Sep 25 '16 at 21:10
  • @AaditMShah fwiw, the present approach required greater effort than the previous attempts, and ironically less `javascript` – guest271314 Sep 26 '16 at 02:04
  • Manually assigning values kind of defeats the purpose doesn't it? – Aadit M Shah Sep 26 '16 at 02:11
  • @AaditMShah Not really. The value can be used on right side, or replaced using `Array.from()` or `.map()` or other process on right side. The only reason to assign an increment value is to acquire the exact `.length` of array to set at right side to fill with values set on left side; without creating an additional variable. A placeholder of sorts, which serves purpose of providing precise `.length` of resulting iterable needed to fill with identifiers.Tried a few other approaches as well. The current approach was simplest, here; does not require function call or loop. – guest271314 Sep 26 '16 at 02:13
  • @AaditMShah Still missing some as to requirement? – guest271314 Sep 26 '16 at 02:25
  • @guest271314 your most recent rewrite makes no sense to me. If you're going to hand-write the assignments, you don't need an array at all. – Mulan Sep 26 '16 at 03:33
  • @naomik The attempt at approach is for assignments to be placeholder values to compute the total number of assignments on left side. Pass that number to right side, where the default value assigned at left side can be changed. Could still use improvement. Tried another approach as well. Will share plnkrs – guest271314 Sep 26 '16 at 04:34
  • @naomik fwiw, what have tried so far plnkr http://plnkr.co/edit/pMpXpPulhW9JXCyqVDkU?p=preview . Version 1 could use improvement, as right side iterates once; version 2 could probably also be improved. – guest271314 Sep 26 '16 at 05:27
  • @naomik Have updated post with some of the approaches tried to return expected result. – guest271314 Sep 26 '16 at 17:54