Search code examples
apldyalog

Dyalog APL: Check if a field exists in JSON?


The task is to check if there is an "s" field in the JSON, I've tried to do this as follows:

   ⍝ Example JSON I'm working with
   JSON ← '{"e":"depthUpdate","E":1699599166770,"s":"BTCUSDT","U":40239636237,"u":40239636314,"b":[["36651.78000000","9.81839000"],["36584.34000000","0.00000000"],["36541.21000000","0.00055000"],["36459.23000000","0.00016000"]],"a":[["36651.79000000","0.47428000"],["36690.27000000","0.00000000"],["36690.96000000","0.00000000"],["36691.33000000", "1.40160000"]]}'
   (⎕JSON JSON).⎕NL¯2  ⍝ Just checking the fields, there's definitely an 's' here
┌─┬─┬─┬─┬─┬─┬─┐
│E│U│a│b│e│s│u│
└─┴─┴─┴─┴─┴─┴─┘
    1=⍴(⍸'s'∘≡¨(⎕JSON JSON).⎕NL¯2) ⍝ Finding
 0

Although this example does work:

      TMP ← 'id' 'result' 's'
      TMP
┌──┬──────┬─┐
│id│result│s│
└──┴──────┴─┘
     1=⍴(⍸'s'∘≡¨TMP)
 1

But why? And how to fix it?

Perhaps there is a more direct way to check if a field exists in JSON?


Solution

  • The reason your code fails is 's' being a scalar (0D array) while any name coming from JSON is a vector (1D array):

          JSON ← '{"e":"depthUpdate","E":1699599166770,"s":"BTCUSDT","U":40239636237,"u":40239636314,"b":[["36651.78000000","9.81839000"],["36584.34000000","0.00000000"],["36541.21000000","0.00055000"],["36459.23000000","0.00016000"]],"a":[["36651.79000000","0.47428000"],["36690.27000000","0.00000000"],["36690.96000000","0.00000000"],["36691.33000000", "1.40160000"]]}'
          1=⍴(⍸(,'s')∘≡¨(⎕JSON JSON).⎕NL¯2)
    1
    

    However, you're actually implementing membership here, so you could use the primitive instead:

          (⊂,'s')∊(⎕JSON JSON).⎕NL¯2
    1
    

    That said, you can ask about the nameclass of a name directly (and that's agnostic about scalar vs vector names). If the nameclass is 0, then the name is not defined. (If it is an object, it is 9, and any other array is 2.) Hence, the normal way to check if a name is defined, is to check for inequality to 0:

          (⎕JSON JSON).⎕NC's'
    2
          0≠(⎕JSON JSON).⎕NC's'
    1
    

    If you're using Dyalog 18.2+, you should use ⎕ATX (full documentation) instead of ⎕NC as it gives more useful results in certain cases. While ⎕ATX can give you a lot of attibutes for a name, code 40 is the nameclass:

          40(⎕JSON JSON).⎕ATX's'
    2
          0≠40(⎕JSON JSON).⎕ATX's'
    1
    

    Attempt this online!

    In our case, ⎕NC actually returns a vector, whereas ⎕ATX returns a more reasonable scalar (though they look the same using the default display form).

          ≢⍴0≠(⎕JSON JSON).⎕NC's'
    1
          ≢⍴0≠40(⎕JSON JSON).⎕ATX's'
    0