Reposting as a corrected version of this question. I have reviewed this, but it didn't help me deal with unknown array nestings.
EDIT: Added information on why Lodash _.get
won't work. Here's a Stackblitz. There's an npm library, Object-Scan that would work -- but, I can't get Angular to play nice with that package.
This is for a column renderer component in an Angular app. The user selects a column to display, and the column is mapped to whatever it is. So, in the below example, if user selects to display "Categories", and it is mapped to "interests[categories]", the function then needs to go find values that correspond to that mapping in the data.
Sample data:
const response = {
id: "1234",
version: "0.1",
drinks: ["Scotch"],
interests: [
{
categories: ["baseball", "football"],
refreshments: {
drinks: ["beer", "soft drink"]
}
},
{
categories: ["movies", "books"],
refreshments: {
drinks: ["coffee", "tea", "soft drink"]
}
}
],
goals: [
{
maxCalories: {
drinks: "350",
pizza: "700"
}
}
]
};
For this data, the possible mappings are:
id
,
version
,
drinks
,
interests[categories]
,
interests[refreshments][drinks]
,
goals[maxCalories][drinks]
,
goals[maxCalories][pizza]
,
Requirement:
I need a recursive function that will work in Angular, and accept two args: one for the data object response
, and a second string of the selector mapping that will return the applicable, potentially nested values, iterating through any objects and/or arrays that are encountered. The mapping string may have either dotted or bracketed notation. Readable, self-documenting code is highly desired.
For example, based on the above data object:
getValues("id", response);
should return["1234"]
getValues("version", response);
should return["0.1"]
getValues("drinks", response);
should return["Scotch"]
getValues("interests.categories", response)
should return["baseball", "football", "movies", "books"]
getValues("interests[refreshments][drinks]", response);
should return["beer", "soft drink", "coffee", "tea", "soft drink" ]
, accounting for however many items may be in theinterests
array.getValues("goals.maxCalories.drinks", response);
should return["350"]
Returned values will ultimately be deduped and sorted.
Original function:
function getValues(mapping, row) {
let rtnValue = mapping
.replace(/\]/g, "")
.split("[")
.map(item => item.split("."))
.reduce((arr, next) => [...arr, ...next], [])
.reduce((obj, key) => obj && obj[key], row)
.sort();
rtnValue = [...new Set(rtnValue)]; //dedupe the return values
return rtnValue.join(", ");
}
The above works fine, just like Lodash' get: if you pass, for example: "interests[0][refreshments][drinks]
, it works perfectly. But, the mapping is not aware of nested arrays and passes in simply "interests[refreshments][drinks]"
, which results in undefined
. Any subsequent array items have to be accounted for.