mathjs icon indicating copy to clipboard operation
mathjs copied to clipboard

Q: How to override property lookup logic (support fields with dots)

Open evil-shrike opened this issue 4 years ago • 4 comments

Hi! I wonder is it possible to override the logic of fields navigation. For example, if I want instead of normal navigation of nested object also support fields with dots in their names. Here's an example, two objects, obj1 - with a nested value, obj2 - with a dot in a field's name. I'd expect the expression "main.temp" works in both cases.

let obj1 = { main: { temp: 280  } }; 
let obj2 = { "main.temp": 280  }; 
let expr = 'main.temp > 273';

Works:

console.log(math.evaluate(expr, obj1));

Doesn't (throws 'Undefined symbol main'):

console.log(math.evaluate(expr, obj2));

evil-shrike avatar Feb 19 '21 13:02 evil-shrike

two nested objects is really different from a key with a dot in the name, and it can lead to ambiguous cases when you want to resolve nested objects. Like here:

let obj3 = { 
  main: { 
    temp: 280  
  }, 
  'main.temp': 260 
};

So what would you expect to get when evaluating let expr = 'main.temp > 273'; in this case?

What you maybe could do is pre-process your scope: replace objects with a dot in the name with nested objects. I.e. replace obj2 with obj in your example.

josdejong avatar Feb 20 '21 11:02 josdejong

Well, yeah, agree it's not very straightforward, that's because I'm asking about customization and don't expect the lib supports this case by default.

In my case I have several data feeds (arrays of object, possibly nested), that I want to join. For this, I flatten them into array-like maps, like this:

// input

feed1: [
  { city: {name: "city1", id: 1}, main: { temp: 280 }},
  { city: {name: "city2" id: 2}, main: { temp: 281 }}
]

// output

feed1: [
{
 "city.name": "city1",
 "city.id":  1,
  "main.temp": 280
},
{
 "city.name": "city2",
 "city.id":  2,
  "main.temp": 281
}

I'm doing this as I have another feed with a foreign key:

feed2: [
  { "city_id": 1, "some_column": "some_value1" },
  { "city_id": 2, "some_column": "some_value2" }
]

and a result feed will be:

result: [
  {
    "city.name": "city1",
    "city.id": 1,
    "main.temp": 280,
    "some_column": "some_value1"
  }, // etc
]

so I don't have any ambiguity, the expr 'main.temp > 273' will address a "column" (technically a field).

I understand that there're workarounds, e.g. I can possible unwind it back to nested objects:

[
 {
    feed1: { city: { name: "city", id: 1}, main: {temp: 280}, feed2: { some_column: "some_value1"} }, //etc
   // or
    { city: { name: "city", id: 1}, main: {temp: 280}, some_column: "some_value1"}, //etc
  }
]

or try to join them without flattening. But there could be other reason for flattening so I wanted to investigate all my options.

evil-shrike avatar Feb 20 '21 11:02 evil-shrike

I understand your use case. I agree that this is a quite specific use case so it makes sense to keep this as a solution (could be a plugin or simply a snippet) separate from mathjs.

I think the most straightforward solution is what you're proposing: do some flattening and merging beforehand yourself to mold the data in the structure that you want to have to expose it inside the expression editor.

josdejong avatar Feb 21 '21 18:02 josdejong

The solution I found for a similar problem was to wrap the object like this:

math.evaluate(`data['main.temp'] > 273`, { data })

chanand avatar Oct 07 '21 10:10 chanand