Chaining multiple conditions

I’ve thought about cascading multiple If but it seems a bit messy:

If(
  //check condition,
  // do something if it's true,
  If(
    //check condition,
    //do something if it's true,
    If(
      //check condition,
      //do something if it's true,
      null
    )
  )
)

Is there a better way to solve more complex situations than if/else?

It’s a good question. How to do early return? I guess a do expression with a load of If(X, Abort(..), true) should work?

There is another Topic about how to emulate a switch statement. Here is a minimal example from that.

1 Like

Wouldn’t that break any nested Dos or UDFs?

Abort cancels the entire query.

1 Like

Jeeessus wth is going on there? :joy:

I have so many questions…

What’s with the array on the bindings?

Is this producing and array with values? But how come then Let simply returns Var('case')?

A fantastical abuse of constructs. Honestly, this is art.

let x;
if ($cond1) x = $expr1
if ($cond2) x = $expr2
if ($cond3) x = $expr3
x

Is basically what is happening. I’m impressed and horrified.

3 Likes

Ben’s example is on point.

If you inspect what the driver does when you call q.Let all Let expressions are converted into this “array syntax”. It’s technically, but not well, documented.

Docs say bindings is a “List of pairs”!

For javascript driver at least, it means an array of [object with exactly 1 key and 1 value].

This “array” version means you can reuse the same binding name, overriding the previous one, but you can still use the previous one with Val.

1 Like

I would however be careful to use that kind of syntax.
Peeking inside a library, looking at what it converts to and using that is indeed technically an abuse of constructs. I am not sure whether it’s guaranteed that this will never change and/or will work in other drivers.

To be clear for people who read this. Normal syntax of a Let is this (this does not work due to the rebinding of ‘case’ while at the same time using it)

Let(
  {
     x: 1
     case: null },
     case: If(Equals(Var('x'), 0), 0, Var('case')), 
     case: If(Equals(Var('x'), 1), 1, Var('case')) , 
     case: If(Equals(Var('x'), 2), 2, Var('case')) 
  }, 
  Var('case')
)

This will throw an error:
image

However if we write a simple Let:

Let({a: 2, b: 3}, Add(Var('a'), Var('b')))

Then look at the request payload

We can see that this translates to the array syntax and in that case rebinding does work.

Let([
    { x: 0 },
    { case: null },
    { case: If(Equals(Var('x'), 0), 0, Var('case')) }, 
    { case: If(Equals(Var('x'), 1), 1, Var('case')) }, 
    { case: If(Equals(Var('x'), 2), 2, Var('case')) }
  ], 
  Var('case')
)

We should probably just allow rebinding in the original syntax instead so you do not have to do this :slight_smile:

1 Like

Until that happens I’d probably be tempted to abuse the host language a little bit (hope I didn’t miss something obviously wrong):

var expressionArr = [
  (x) => { return { expr: Equals(x, 0), result: 'a'}},
  (x) => { return { expr: Equals(x, 1), result: 'b'}},
  (x) => { return { expr: true, result: 'c'}}
]

function Switch(expressionArr, value){
  return Select([0, 'result'], Filter(expressionArr.map((el) => el(value)), Lambda(['expAndRes'],  Select(['expr'], Var('expAndRes')))))
}

Switch(expressionArr, 1)
2 Likes

This is a cool way to do it! Type systems will be happier and it wouldn’t require changes for the string representation to look proper.

The docs specifically say “List of pairs”. To that end it seems like providing a Map is an abuse of constructs. :nerd_face: