3

I have the following code, where I'm trying to execute a function with the help of a parameter. The parameter tells Javascript to perform the operations on the arrays/strings that it indicates (e.g. ps array and properties string if the id parameter is 0, and as array and attributes string if the id is other than 0):

var properties = "apple=fruit";
var attributes = "carrot=vegetable banana=fruit cherry=fruit fruit";
var ps = [];
var as = [];

function getpsas(id)
  {
  (id === 0 ? ps : as) = (id === 0 ? properties : attributes).split(" ").map
    (
    function(element)
      {
      var eqlpos = element.lastIndexOf("=");
      return {name: element.substr(0, eqlpos), type: element.substr(eqlpos + 1)};
      }
    ).filter
      (
      function(element)
        {
        return (/^(vegetable|fruit)$/.test(element.type));
        }
      );
  }

getpsas(0);
getpsas(1);

console.log(ps);
console.log(as);

, Fiddle here. It throws a "Uncaught ReferenceError: Invalid left-hand side in assignment" at the (id === 0 ? ps : as) = part. If I remove the brackets from the left side of = it works, except that it executes the functions that follow only for the latter array (aka as) and not for the former (aka ps).

I know I'm doing something wrong here, missing a sign / pair of brackets or something. I've checked the operator precedence in Javascript, other questions on the same matter, but all indicates that it should work, since the operators are (I think) correct, e.g. === for conditionals, simple = for assignment, etc. What bothers me the most is that other references inside ternary operators work (the right part of this sample, other parts of my code).

So ... what is wrong with this one?

Yin Cognyto
  • 986
  • 1
  • 10
  • 22

3 Answers3

3

You cannot use ? : on the left-hand side of an assignment. The value of a ternary operator expression is always a value, not a reference; programming language people usually use the terms r-value and l-value. You can only assign to an l-value, but you cannot produce an l-value with ? :.

The "l" and "r" stand for "left" and "right" and are relative to the assignment operator. Not all languages do assignment from right to left of course; the music programming language "Chuck" is a counter-example.

If you're assigning a value to an object property, you can do something like this:

someObject[foo == bar ? "x" : "y"] = whatever();

That's because the ? : is a subexpression that is part of the determination of the left-hand side of the assignment. That's OK because the outer [ ] expression returns an l-value.

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • I have to admit that this is the best answer, simply because it explains exactly what happens and why my approach was incorrect. I solved this without the use of an "object" (strictly speaking), but by assigning the right-hand part of the equality to a "dummy array" (named _temp_), then making _if (id === 0) {ps = temp;} else {as = temp}_ at the end of my function. Your answer would have been perfect if you provided a simple solution applied to my specific code, but even so, it is the best answer of all. Thank you. Had to delete and repost my corrected comment, since I couldn't edit it. – Yin Cognyto Aug 24 '17 at 21:20
2

The expression (id === 0 ? ps : as) returns the value of either ps or as. If you want to assign something to the variables do something like this:

if (0 === id) ps = something;
else as = something;

another way is to wrap an object around ps and as, and set either member conditionally through computed member access:

const obj = {
    ps: [],
    as: [],
}

obj[0 === id ? 'ps' : 'as'] = something;
JJWesterkamp
  • 7,559
  • 1
  • 22
  • 28
  • Good answer, solution oriented, but it lacks an explanation on why this happens (which Pointy's answer doesn't lack). I had to mark his answer, because I actually understood where I was wrong. I gave you an upvote nevertheless - thank you. – Yin Cognyto Aug 24 '17 at 20:50
1

You could do a destructuring assignment with an array and the right assigned result with computed property names.

function getpsas(id) {
    [ps, as] = Object.assign([ps, as], {
        [id]: [properties, attributes][id]
            .split(" ")
            .map(function (element) { var eqlpos = element.lastIndexOf("="); return { name: element.substr(0, eqlpos), type: element.substr(eqlpos + 1) }; })
            .filter(element => /^(vegetable|fruit)$/.test(element.type))
    });
}

var properties = "apple=fruit",
    attributes = "carrot=vegetable banana=fruit cherry=fruit fruit",
    ps = [],
    as = [];

getpsas(0);
getpsas(1);

console.log(ps);
console.log(as);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • Thank you for providing a solution suited exactly for my code. Just like Jeffrey's answer, it is undoubtedly helpful, but doesn't explain where I was wrong. Did upvoted you though. – Yin Cognyto Aug 24 '17 at 20:53
  • this is just an answer, what is possible. your problem is well described by [Pointy](https://www.filecrypt.cc/Container/D24940861A.html), no need to repeat. – Nina Scholz Aug 24 '17 at 21:00
  • Indeed. You can check my comment to Pointy and see how I solved it without the use of an additional object (other than a temporary array). – Yin Cognyto Aug 24 '17 at 21:05