How to simplify ternary expressions in Javascript

5

0

I have an expression that could be expressed as either of :

a += (A ? B ? x : C ? y : D : D);

a += (A && B ? x : A && C ? y : D);

where A,B,C are expressions of 5-10 bytes each, and x and y are single character literals (3-4 bytes). D is another chain of ternaries (without the branching problem).

I'm getting stuck trying to eliminate the duplication of D or A. If I was using if, it would be something like this:

if (A)
    if (B)
        x
    else if (C)
        y
else D

Obviously I could do ((z=A) && B ? x : z && C ? y : D)...but any other more creative suggestions?

The actual code looks something like:

if (r%4<2&&r>3&&c<22&&c>1)
    if ((i-r)%8==6)
        '\\'
    else if ((i+r)%8==1)
        '/'
else

D is something like:

(i+r) % 8 == 3 ? '/' : 
    (c-r+16) % 8 == 4 ? '\\' : 

Steve Bennett

Posted 2017-05-18T03:55:09.333

Reputation: 1 558

It would help to know what those expressions are. – Leaky Nun – 2017-05-18T03:57:24.063

yup, updated. try not to distracted by golfing them :) – Steve Bennett – 2017-05-18T03:58:58.350

Well, (i-r)%8 can become i-r&7 :p – Leaky Nun – 2017-05-18T03:59:34.653

I still can't see what your D is. – Leaky Nun – 2017-05-18T04:00:30.443

Updated again... – Steve Bennett – 2017-05-18T04:01:54.917

You're still hiding part of your D. – Leaky Nun – 2017-05-18T04:02:21.007

Answers

2

If you know that x and y cannot contain falsy values, as your code examples suggest, you can do the following to eliminate duplicate evaluation of A or D:

a += A && (B && x || C && y) || D;

Demo code and test cases in the following snippet:

// return x
test(1,1,0); // A and B
test(1,1,1); // A and B (and C)

// return y
test(1,0,1); // A and C

// return D
test(0,0,0); // 
test(0,0,1); // C
test(0,1,0); // B
test(0,1,1); // B and C
test(1,0,0); // A


function test(A, B, C) {
  var x = 'x', y = 'y', D = 'D';
  a = A && (B && x || C && y) || D;
  expected = (A && B ? x : A && C ? y : D);
  var ws = ' ';
  console.log(
    [
      A && 'A' || ws, 
      B && 'B' || ws, 
      C && 'C' || ws
    ].join(ws),
    ' ==> a = ' + a,
    a === expected ? 
      'Passed' : 
      'Failed, expected ' + expected
  );
}

Otherwise, if falsy values are possible, you could do this:

a += [D, x, y][A && (B && 1 || C && 2) || 0];

Demo code and test cases in the following snippet:

// return x
test(1,1,0); // A and B
test(1,1,1); // A and B (and C)

// return y
test(1,0,1); // A and C

// return D
test(0,0,0); // 
test(0,0,1); // C
test(0,1,0); // B
test(0,1,1); // B and C
test(1,0,0); // A


function test(A, B, C) {
  var x = false, y = 0, D = '';
  A = !!A; B = !!B; C = !!C;
  a = [D, x, y][A && (B && 1 || C && 2) || 0];
  expected = (A && B ? x : A && C ? y : D);
  var ws = ' ';
  console.log(
    [
      A && 'A' || ws, 
      B && 'B' || ws, 
      C && 'C' || ws
    ].join(ws),
    ' ==> a = ' + xyD(a, x, y, D),
    a === expected ? 
      'Passed' : 
      'Failed, expected ' + xyD(expected, x, y, D)
  );
}

function xyD(result, x, y, D) {
  return result === x ? 'x' :
         result === y ? 'y' :
         result === D ? 'D' : '<error>' ;
}

Tomas Langkaas

Posted 2017-05-18T03:55:09.333

Reputation: 324

Thanks, that's very helpful. So, probably I should just stop thinking in terms of ternaries altogether. – Steve Bennett – 2017-06-12T22:23:17.980

1

Since short-circuiting is not required here, you can also do:

d=D;a+=A?B?x:C?y:d:d;

If short-circuiting were required, since your B and C are truthy values, you can do:

a+=(A?B?x:C?y:0:0)||D;

If short-circuiting were required and your B and C are not truthy values:

d=_=>D;a+=A?B?x:C?y:d():d();

Leaky Nun

Posted 2017-05-18T03:55:09.333

Reputation: 45 011

Ah, that second one is an interesting way to "fall through" a ternary operator. – Steve Bennett – 2017-05-18T04:25:56.927