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!

9
  • 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. Commented Jul 21, 2019 at 20:41
  • 3
    ^ is not a logical operator. instead it's a bitwise XOR ^ operator. Commented Jul 21, 2019 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. Commented Jul 21, 2019 at 20:46
  • 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. Commented Jul 21, 2019 at 20:51
  • 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 Commented Jul 21, 2019 at 21:13

4 Answers 4

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.

Sign up to request clarification or add additional context in comments.

5 Comments

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 according to some other StackOverflow answers (e.g. stackoverflow.com/questions/7820683/… - see the first comment), the unary + operator is up to 97% slower, so I chose | 0.
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.
As expected, once you do the microbenchmarks properly, all of the test cases have the same performance. The 97% difference was only in very old engine versions that apparently couldn't optimise one as well as the others.
@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.
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);

1 Comment

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)
-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)

10 Comments

That separation by the comma operator is really weird. Why not just use two statements, and semicolons?
@Bergi what's wrong with a comma? Looks short. precise, subtle to me. Also saves you a line
I don't see what's wrong with this answer. Separation by comma is very common in JavaScript...
@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 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.
|
-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.

1 Comment

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.