Good day everyone! I have a problem with a search function on JavaScript.
This is an object I have (states):
{
"1": {
"id": "1",
"name": "Category #1",
"hasChild": "Y",
"count": "0",
"parentId": null,
"link": "/catalog/",
"subcategories": [
{
"id": "21",
"name": "Subcategory #1",
"hasChild": "Y",
"count": "0",
"parentId": "1",
"link": "/catalog/",
"subcategories": [
{
"id": "24",
"name": "subsubcategory #1",
"hasChild": "Y",
"count": "1",
"parentId": "21",
"link": "/catalog/",
"subcategories": [],
},
{
"id": "25",
"name": "subsubcategory #2",
"hasChild": "Y",
"count": "0",
"parentId": "21",
"link": "/catalog/",
"subcategories": [],
}
],
},
{
"id": "22",
"name": "Subcategory #2",
"hasChild": "Y",
"count": "0",
"parentId": "1",
"link": "/catalog/",
},
{
"id": "23",
"name": "Subcategory #3",
"hasChild": "Y",
"count": "0",
"parentId": "1",
"link": "/catalog/",
}
],
},
"2": {
"id": "2",
"name": "Category #2",
"hasChild": "Y",
"count": "0",
"parentId": null,
"link": "/catalog/",
"subcategories": [
..
],
},
}
And I have an array of products to which one has an id of the category to which it belongs. So I extracted from there only unique values of categories. It can be any level.
["24", "22", "2" ...]
My goal is to take the "name" values of parents' categories.
Example: product is in a category with id:24 (name: subsubcategory #1).
How can I get the value "Category #1" from the top category?
I use that function, but it only work for me on 1-st level (if id:1 or 2)
function filter(item, search, textKey) {
let result = []
const _filter = (item, search, textKey) => {
for (const i of item) {
if (i[textKey].indexOf(search) !== -1) {
result = [...result, { name: i.name, id: i.id, parentId: i.parentId }]
}
i.children ? _filter(i.children, search, textKey) : null
}
}
_filter(item, search, textKey)
return result
}
console.log(filter(Object.values(states), '24', 'id')) // didn't work
console.log(filter(Object.values(states), '2', 'id')) // found and mapped
CodePudding user response:
I would build a function that finds the names associated with a list of ids by mapping over a function that finds the names associated with a single id. That I would build atop a function which recursively finds the full ancestor node based on an arbitrary predicate.
Your initial input is not quite a recursive structure. It looks like it might be a bad copy from console output, although it could still be legitimate. In any case, we do a transformation first, using Object .values
to extract an array of actual category nodes. This could be moved from one level of the call chain to another, depending on what level of reuse you want from these functions. If your data is in fact an array, this will still work, but I would suggest replacing Object .values (states)
with just states
, as it makes for cleaner code.
const rootAncestor = (pred) => (xs) =>
xs .find (x => pred (x) || rootAncestor (pred) (x .subcategories || []))
const rootAncestorName = (states) => (id) =>
rootAncestor (x => x .id == id) (states) ?.name ?? ''
const rootAncestorNames = (states) => (ids) =>
ids .map (rootAncestorName (Object .values (states)))
const states = {1: {id: "1", name: "Category #1", hasChild: "Y", count: "0", parentId: null, link: "/catalog/", subcategories: [{id: "21", name: "Subcategory #1", hasChild: "Y", count: "0", parentId: "1", link: "/catalog/", subcategories: [{id: "24", name: "subsubcategory #1", hasChild: "Y", count: "1", parentId: "21", link: "/catalog/", subcategories: []}, {id: "25", name: "subsubcategory #2", hasChild: "Y", count: "0", parentId: "21", link: "/catalog/", subcategories: []}]}, {id: "22", name: "Subcategory #2", hasChild: "Y", count: "0", parentId: "1", link: "/catalog/"}, {id: "23", name: "Subcategory #3", hasChild: "Y", count: "0", parentId: "1", link: "/catalog/"}]}, 2: {id: "2", name: "Category #2", hasChild: "Y", count: "0", parentId: null, link: "/catalog/", subcategories: []}}
console .log (rootAncestorNames (states) (['24', '22', '2', '42']))
I added a failing lookup with 42
to show that I make the guess that we want to return an empty string. But at the end of rootAncestorName
, you could replace that with null
, undefined
, or some other token.