2

Apologies for the malformed title, I can't really think of a better way to describe what I mean to say. Here is my current code:

fromEn = true; // some boolean value

options = {
  from: fromEn ? "en" : "es",
  to: fromEn ? "es" : "en"
};

I want from to be "en" when fromEn is true and "es" when it's false. to should be the "opposite value" of from, so-to-speak. I feel my current code is too redundant, and although it works I'd like a more elegant solution. Is there a better way of achieving what I'm trying to do?

EDIT: I decided that ternaries are the best way to go. See Emissary's solution for what I've chosen to do, and see my answer lower to find three possible solutions. Thank you all for the help!

101arrowz
  • 1,367
  • 3
  • 13
  • 2
    you could do something like `const args = ["es", "en"]; options = { from: args[0^fromEn], to: args[1^fromEn]}` but if this is any better? I'd doubt it. I'd bet that even you would wonder what this is/does in like 2 months from now. – Thomas Jul 21 '19 at 20:41
  • @Thomas that's the kind of solution I was looking for, only a little more understandable :) But thank you for the idea, I might try to do some operator shenanigans – 101arrowz Jul 21 '19 at 20:43
  • 3
    `^` is not a logical operator. instead it's a [bitwise XOR `^`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Referen.ce/Operators/Bitwise_Operators#(Bitwise_XOR)) operator. – Nina Scholz Jul 21 '19 at 20:46
  • 4
    I'd find `options = fromEn ? {from:'en',to:'es'} : {from:'es',to:'en'}` more idiomatic though it's really down to personal preference. – Emissary Jul 21 '19 at 20:46
  • @Emissary I agree that is better, and if I can't find a better solution I'll use that instead. – 101arrowz Jul 21 '19 at 20:47
  • 1
    @Emissary right, or maybe `const [from, to] = fromEn? ["en", "es"]: ["es", "en"]; options = { from, to };` or @101arrows if you're still on the lookout for dirty hacks that you won't understand anymore when taking a look at, the next time: `const [from, to] = ["en", "es", "en"].slice(+fromEn); options = { from, to };`. I like to play around with that kind of hacks, but these should never turn up in production code. – Thomas Jul 21 '19 at 20:51
  • Actually your way is fine and dynamic, any other suggestion would be just preferences to the developer, Again your code is good dynamic and very understandable – Mohamed Abdallah Jul 21 '19 at 20:53
  • 1
    Not sure what was gained by all these different variations other than an academic exercise and using roughly the same amount of characters to arrive at same results – charlietfl Jul 21 '19 at 21:13
  • @charlietfl as someone who cares immensely about liking their own code, I can say that many of these answers look cleaner to me. It really isn't going to do anything significant for speed or file size, you are correct, but that's not the point. – 101arrowz Jul 21 '19 at 21:17

4 Answers4

1

After some research I have three possible solutions.

1st (the one I will use - thanks Emissary!)

options = fromEn ? {from:'en',to:'es'} : {from:'es',to:'en'}

2nd (one I came up with Bergi suggested - originally used bitwise OR against 0 |0)

const langs = ["es", "en"];
const opts = {
  from: langs[+fromEn],
  to: langs[+!fromEn]
}

3rd (using bitwise XOR - thanks Thomas!)

const langs = ["es", "en"];
options = { from: langs[0^fromEn], to: langs[1^fromEn]};

2nd and 3rd are fairly unreadable, so for the sake of readability #1 is my preference.

101arrowz
  • 1,367
  • 3
  • 13
  • I'd do `langs[+fromEn]` / `langs[+!fromEn]` for the number conversion, instead of `|0` (which is usually used specifically for integer conversion). But both are fine I guess, and much less weird than the XOR. – Bergi Jul 21 '19 at 21:00
  • @Bergi according to some other StackOverflow answers (e.g. https://stackoverflow.com/questions/7820683/convert-boolean-result-into-number-integer/7820695#7820695 - see the first comment), the unary `+` operator is up to 97% slower, so I chose `| 0`. – 101arrowz Jul 21 '19 at 21:02
  • I doubt that's true (at least, measuring the conversion itself and not the subsequent usage of the number), but most importantly they do different things so beware of premature optimisation. – Bergi Jul 21 '19 at 21:05
  • As expected, once you [do the microbenchmarks properly](https://mrale.ph/blog/2012/12/15/microbenchmarks-fairy-tale.html), all of the test cases [have the same performance](https://jsperf.com/boolean-int-conversion/13). The 97% difference was only in very old engine versions that apparently couldn't optimise one as well as the others. – Bergi Jul 21 '19 at 21:16
  • 2
    @Bergi I stand corrected. The difference is essentially negligible, you are correct. Since the `+` operator is more readable and common it is the better choice. – 101arrowz Jul 21 '19 at 21:23
0

You could use property getters. This way to from and to will vary dynamically, depending on the value of the fromEn flag - which I have moved into the options object, keeping things together that belong together.

let options = {
  fromEn: true,
  get from() { return this.fromEn ? "en" : "es" },
  get to() { return this.fromEn ? "es" : "en" }
};

console.log("From " + options.from + " to " + options.to);

options.fromEn = false;

console.log("From " + options.from + " to " + options.to);
Peter B
  • 18,964
  • 5
  • 26
  • 60
  • 1
    Thank you for your answer, Peter! I was asking more for a way to avoid the double ternaries rather than to dynamically change the values of `options`, but I'll use this in the future for sure (never knew that js getters were so easy to make) – 101arrowz Jul 21 '19 at 20:52
-1

How about just

fromEn = true;
let options = {}
options.from = fromEn ? "en" : "es", options.to = fromEn ? "es" : "en"
console.log(options)

Add the properties manually to the object, separated by a comma. You can also do

fromEn = true;
let options = fromEn ? {from:"en",to:"es"} : {from:"es",to:"en"}
console.log(options)
weegee
  • 2,511
  • 1
  • 15
  • 31
  • That separation by the comma operator is really weird. Why not just use two statements, and semicolons? – Bergi Jul 21 '19 at 21:17
  • @Bergi what's wrong with a comma? Looks short. precise, subtle to me. Also saves you a line – weegee Jul 21 '19 at 21:19
  • 1
    I don't see what's wrong with this answer. Separation by comma is very common in JavaScript... – 101arrowz Jul 21 '19 at 21:19
  • @weegee Replacing the comma by a semicolon is just as short and precise, but less subtle. The comma operator also is ambiguous if you don't know the precedence between the conditional operator and the comma operator. Saving lines is the job of minifiers, not of developers. The second snippet is much better in *all* regards. – Bergi Jul 21 '19 at 21:24
  • @Bergi I didn't understand the last comment but if go about fiddling with a javascript miniifer, for example `if(x){foo();return bar()}else{return 1}` is turned to `return x?(foo(),bar()):1` Bytes saved. – weegee Jul 21 '19 at 21:27
  • @weegee That's just my point. Saving bytes and obfuscation can be done with a minifier. As a developer, you should focus on writing readable code, and usage of the comma operator is rarely that. – Bergi Jul 21 '19 at 21:35
  • @Bergi yes. I can't see how by just adding a comma is making this code, not readable by anyone. I've been using commas for a long time now even when declaring variables. instead of typing `let` again and again, type it once and separate the rest by commas. They were meant to be in javascript to be *used* by developers. Not just for minifiers – weegee Jul 21 '19 at 21:37
  • @weegee I just say that `options.from = fromEn ? "en" : "es"; options.to = fromEn ? "es" : "en"` (with a semicolon) would be even more readable. Or just put separate statements on two lines. Notice that the comma in variable declarations or function calls is not the comma operator. – Bergi Jul 21 '19 at 21:39
  • @Bergi yes comma operator is different but I'm talking about the usage of the **keyword** in the language's general context. Those two would still be the same and I can't see how using a semi-colon will make it more readable. – weegee Jul 21 '19 at 21:41
  • It just follows the principle of the least surprise. The comma operator is relatively uncommon, and given that there is no necessity to use it here the average developer will be surprised and wonder for what purpose you used it here. It just raises the cognitive load. – Bergi Jul 21 '19 at 21:51
-2

Please declare variables with either const or let so that those variable don't pollute the scope.

The answer to your question, toggle the fromEn.

const fromEn = true; // some boolean value

const options = {
  from: fromEn ? "en" : "es",
  to: !fromEn ? "en" : "es"
};

Hope this helps.

Sai
  • 1
  • 1
    Thanks for your answer, and welcome to Stack Overflow! I appreciate your solution, but it is virtually identical to mine: using ternaries in both values, evaluating `fromEn` twice. A single exclamation point differs your answer from my question. I was looking for a more elegant way of doing things rather than two ternaries. Regardless, thank you for your advice about using `const` and `let`. – 101arrowz Jul 21 '19 at 20:39