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
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.
Related
Hey guys I have the following array that's used to display a flatlist within my app.
Array [
Object {
"data": "Item 1",
"id": "1",
"subjects": "1,8,9,23,11,15,16,14,20",
},
Object {
"data": "Item 2",
"id": "2",
"subjects": "8,11,2,4,16,19",
},
Object {
"data": "Item 3",
"id": "3",
"subjects": "16,20,14,11,9,2",
},
Object {
"data": "Item 4",
"id": "4",
"subjects": "1,16,19",
},
]
However I would like to sort this array based off the subjects value. In the app the user can select a couple of subjects which are represented by numbers so lets say the users selected subjects are:
11, 4, 2, 1
I would like to sort the array so that the items with 3 or more subjects in common with the user are sorted to the top and then items with two and then 1 and then none so the array above should look like this after sorting:
Array [
Object {
"data": "Item 2",
"id": "2",
"subjects": "8,11,2,4,16,19",
},
Object {
"data": "Item 1",
"id": "1",
"subjects": "1,8,9,23,11,15,16,14,20",
},
Object {
"data": "Item 3",
"id": "3",
"subjects": "16,20,14,11,9,2",
},
Object {
"data": "Item 4",
"id": "4",
"subjects": "0,16,19",
},
]
How can I achieve this?
I have been searching around the array sort function:
Array.prototype.sort()
However I have only seen how to sort based off number comparisons I have never seen an array sorted based off values in common. Please could someone help me with this!
EDIT
Array [
Object {
"data": "Item 2",
"id": "2",
"subjects": "8,11,2,4,16,19",
"ranking": "green",
},
Object {
"data": "Item 1",
"id": "1",
"subjects": "1,8,9,23,11,15,16,14,20",
"ranking": "amber",
},
Object {
"data": "Item 3",
"id": "3",
"subjects": "16,20,14,11,9,2",
"ranking": "amber",
},
Object {
"data": "Item 4",
"id": "4",
"subjects": "0,16,19",
"ranking": "red",
},
]
You could create an object with counts of selected subjects and sort descending by this value.
const
data = [{ data: "Item 1", id: "1", subjects: "1,8,9,23,11,15,16,14,20" }, { data: "Item 2", id: "2", subjects: "8,11,2,4,16,19" }, { data: "Item 3", id: "3", subjects: "16,20,14,11,9,2" }, { data: "Item 4", id: "4", subjects: "1,16,19" }],
selected = [11, 4, 2, 1],
counts = data.reduce((r, { id, subjects }) => {
r[id] = subjects
.split(',')
.reduce((s, v) => s + selected.includes(+v), 0);
return r;
}, {});
data.sort((a, b) => counts[b.id] - counts[a.id]);
console.log(data);
console.log(counts);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have an array of objects who follow this structure below:
{
"level": 1
"objectId": "3756"
"objectIdNo": 35636
"wpId": "3635473"
}
I now want to filter an array of these objects by another object. This filterObject would have the structure below:
// filterObject
{
level: "2"
objectId: "10"
wpId: "1"
}
But this filterObject doesn't always have all the key-value pairs because they get set manually in the UI. As a result the filterObject can also look like this:
{
level: "2"
}
My goal is to return a new array of filteredObjects who match this filterObject. When only one filter exists on the filterObject I want to return all objects that match this one key-value pair. But if more filters exist on the filterObject I want to return all objects that match both key-value pairs (not only one).
Example:
This is the data I want to filter:
[
{
"level": "1"
"objectId": "11"
"objectIdNo": "320"
"wpId": "123"
},
{
"level": "2"
"objectId": "12"
"objectIdNo": "321"
"wpId": "123"
},
{
"level": "2"
"objectId": "13"
"objectIdNo": "322"
"wpId": "120"
},
]
1.
If this is my filterObject:
{
"level": "2"
}
Return:
[
{
"level": "2"
"objectId": "12"
"objectIdNo": "321"
"wpId": "123"
},
{
"level": "2"
"objectId": "13"
"objectIdNo": "322"
"wpId": "120"
},
]
2.
If this is my filterObject:
{
"level": "2",
"wpId": "123"
}
Return:
[
{
"level": "2"
"objectId": "12"
"objectIdNo": "321"
"wpId": "123"
},
]
I hope that explains the logic I want to achieve which I couldn't implement myself. I would appreciate some ideas or applicable functions.
This is what I already tried in React. The data variable holds the array of objects and the filter variable hold the filterObjects.
useEffect(() => {
if (data) {
const filtered = data.filter((task) => {
if (!filter) {
return true;
}
return (
task.level === filter.level ||
task.objectId === filter.objectId ||
task.wpId === filter.wpId
);
});
setFilteredTasks(filtered);
}
}, [filter]);
With my attempt, if I just set the one filter key-value pair I get an empty array,
You can achieve this result using filter, Object.keys, and every.
You have to use filter and pass predicate that tell whether it is included in the final result.
In predicate, loop over all properties on the filters object and match if it is present in data or not. Simple
data.filter((o) =>Object.keys(filters).every((k) => filters[k] === o[k]));
const data = [{
level: "1",
objectId: "11",
objectIdNo: "320",
wpId: "123",
},
{
level: "2",
objectId: "12",
objectIdNo: "321",
wpId: "123",
},
{
level: "2",
objectId: "13",
objectIdNo: "322",
wpId: "120",
},
];
const filters = {
level: "2",
wpId: "123",
};
const result = data.filter((o) =>
Object.keys(filters).every((k) => filters[k] === o[k])
);
console.log(result);
This should do the trick!
const exampleData = [
{
"level": "1",
"objectId": "11",
"objectIdNo": "320",
"wpId": "123",
},
{
"level": "2",
"objectId": "12",
"objectIdNo": "321",
"wpId": "123",
},
{
"level": "2",
"objectId": "13",
"objectIdNo": "322",
"wpId": "120",
},
];
const filterObject1 = {
"level": "2",
}
const filterObject2 = {
"level": "2",
"wpId": "123"
}
function filter(data, filterObject) {
const filterValues = Object.entries(filterObject)
let filteredData = data
for(const [filterKey, filterValue] of filterValues) {
filteredData = filteredData.filter(obj => obj[filterKey] === filterValue)
}
return filteredData
}
console.log(filter(exampleData, filterObject1))
console.log(filter(exampleData, filterObject2))
You can do like this:
const data = [
{
level: "1",
objectId: "11",
objectIdNo: "320",
wpId: "123",
},
{
level: "2",
objectId: "12",
objectIdNo: "321",
wpId: "123",
},
{
level: "2",
objectId: "13",
objectIdNo: "322",
wpId: "120",
},
];
const filterObject = {
level: "2",
wpId: "123",
};
const result = data.filter((item) => {
let flag = true;
Object.keys(filterObject).forEach((key) => {
if (item[key] !== filterObject[key]) {
flag = false;
return;
}
});
return flag;
});
console.log(result);
const input = [ { "level": "1", "objectId": "11", "objectIdNo": "320", "wpId": "123" }, { "level": "2", "objectId": "12", "objectIdNo": "321", "wpId": "123", }, { "level": "2", "objectId": "13", "objectIdNo": "322", "wpId": "120" }, ]
const filter = { "level": "2", "wpId": "123" };
const filteredOutput = input.filter( obj => {
return Object.keys(filter).every( filterKeys => {
return obj[filterKeys] === filter[filterKeys]
});
});
console.log(filteredOutput);
How can I remove sub-objects?
[{
"id": "1",
"desc": "SOME PRODUCT",
"codigo": "CODE-28",
"codigoBarras": "2000000001",
"unidade": "PCT",
"price": "24.15",
"current_inventory": [{
"2kg": "5",
"5kg": "5",
"10kg": "5",
"20kg": "5",
"productId": "1"
}]
}]
[{
"id": "1",
"desc": "SOME PRODUCT",
"codigo": "CODE-28",
"codigoBarras": "2000000001",
"unidade": "PCT",
"price": "24.15",
"current_inventory_2kg": "5",
"current_inventory_5kg": "5",
"current_inventory_10kg": "5",
"current_inventory_20kg": "5",
}]
Use Object.keys() and a forEach loop
var x =[
{
"id": "1",
"desc": "SOME PRODUCT",
"codigo": "CODE-28",
"codigoBarras": "2000000001",
"unidade": "PCT",
"price": "24.15",
"current_inventory": [
{
"2kg": "5",
"5kg": "5",
"10kg": "5",
"20kg": "5",
"productId": "1"
}
]
}
]
x[0].current_inventory.forEach(e=>{
Object.keys(e).forEach(j=>{
x[0]['current_inventory_'+j]=e[j];
})
delete x[0].current_inventory
})
console.log(x)
Use Object.entries and reduce will simplify.
const data = [
{
id: "1",
desc: "SOME PRODUCT",
codigo: "CODE-28",
codigoBarras: "2000000001",
unidade: "PCT",
price: "24.15",
current_inventory: [
{
"2kg": 5,
"5kg": 5,
"10kg": 5,
"20kg": 5,
productId: 1
}
]
}
];
const [first] = data;
const updated = Object.entries(first).reduce((acc, [key, value]) => {
if (Array.isArray(value)) {
value.forEach(item =>
Object.entries(item).forEach(
([cKey, cValue]) => (acc[`${key}_${cKey}`] = cValue)
)
);
} else {
acc[key] = value;
}
return acc;
}, {});
console.log(updated);
THANK YOU VERY MUCH!!!
Solved using the code below:
data is de object
estoqueFracionado is the sub-object
for (let [key, value] of Object.entries(data)) {
value.estoqueFracionado.forEach (e => {
Object.keys(e).forEach(j => {
value['estoqueFracionado_' + j] = e[j]
})
delete value.estoqueFracionado
})
}
I have an object which comprises of a menu.
I want to enter a category ID and get the category name, then move backwards to find it's parents. That's not easy within an object so I'm thinking to catch the parents along the way instead.
The problem I have is how to reset the parents when the end child is not found and there's nowhere else to go.
This is what I'm trying:
var data = [
{
"tree_id": "10",
"name": "babies & children",
"parent": null,
"position": "1"
}, {
"tree_id": "2",
"name": "clothing",
"parent": null,
"position": "1",
"children": [{
"tree_id": "15",
"name": "kids",
"parent": "2",
"position": "3",
"children": [{
"tree_id": "78",
"name": "fourToTen",
"parent": "15",
"position": "3",
"children": [{
"tree_id": "102",
"name": "fourToSix",
"parent": "78",
"position": "3"
}]
}]
}]
}, {
"tree_id": "55",
"name": "toys",
"parent": null,
"position": "1",
"children": [{
"tree_id": "35",
"name": "lego",
"parent": "55",
"position": "3"
}]
}
];
var crumbs = [];
function getParts(data, elem) {
for(var i = 0; i < data.length; i++) {
var obj = data[i];
if(obj.children !== undefined){
/* push parent into crumbs */
crumbs.push(obj.name);
if(obj.children[0].tree_id === elem){
/* if we've found what we're looking, we're done */
crumbs.push(obj.children[0].name);
console.log(crumbs);
} else {
/* reset parents */
crumbs = []; /* <-- this is wrong here */
/* not found, keep recursing */
getParts(obj.children, elem);
}
}
}
}
/* I want this to return
[
"clothing",
"kids",
"fourToTen",
"fourToSix"
]
but it returns
[
"fourToTen",
"fourToSix"
]
*/
getParts(data, '102');
The question is, how can I save the parents array until I'm at the end of the line and the child is not found, and reset it then?
Here's a fiddle if that's your preferred playround
Assuming category id = tree_id and category_name = name
You'll need to treat your data object like a tree, then transverse it and track the parents along the way. If something is found then dump the information you need.
So data is basically an array of objects you will be transversing.
Example:
"use strict";
var data = [
{
"tree_id": "10",
"name": "babies & children",
"parent": null,
"position": "1"
},
{
"tree_id": "2",
"name": "clothing",
"parent": null,
"position": "1",
"children": [{
"tree_id": "15",
"name": "kids",
"parent": "2",
"position": "3",
"children": [{
"tree_id": "78",
"name": "fourToTen",
"parent": "15",
"position": "3",
"children": [{
"tree_id": "102",
"name": "fourToSix",
"parent": "78",
"position": "3"
}]
}]
}]
},
{
"tree_id": "55",
"name": "toys",
"parent": null,
"position": "1",
"children": [{
"tree_id": "35",
"name": "lego",
"parent": "55",
"position": "3"
}]
}
];
// Solution
function transverse(root, tree, targetId) {
tree.push({
catId : root.tree_id,
catName : root.name
});
/* this if() must come first otherwise fails if you want to stop before end */
if (root.tree_id === targetId) {
console.log("Found id:" + targetId+ ", name=" + root.name);
console.log("Dumping parent info => " + JSON.stringify(tree));
return tree;
}
if (root.hasOwnProperty("children") && root.children instanceof Array)
root.children.forEach(child => {
transverse(child, tree, targetId);
});
}
data.forEach(item => {
transverse(item, [], /*Looking for Id=*/"102");
});
console.log("done");
Output:
Found id:102, name=fourToSix
Dumping parent info =>
[
{"catId":"2","catName":"clothing"},
{"catId":"15","catName":"kids"},
{"catId":"78","catName":"fourToTen"},
{"catId":"102","catName":"fourToSix"}]
]
Here's a compact functional way:
data = [{"tree_id":"10","name":"babies & children","parent":null,"position":"1"},{"tree_id":"2","name":"clothing","parent":null,"position":"1","children":[{"tree_id":"15","name":"kids","parent":"2","position":"3","children":[{"tree_id":"78","name":"fourToTen","parent":"15","position":"3","children":[{"tree_id":"102","name":"fourToSix","parent":"78","position":"3"}]}]}]},{"tree_id":"55","name":"toys","parent":null,"position":"1","children":[{"tree_id":"35","name":"lego","parent":"55","position":"3"}]}]
//
first = (ary, fn) => ary.reduce((r, x) => r || fn(x), false);
locate = (data, id) => _locate({children: data}, id, []);
_locate = (node, id, path) => node.tree_id === id ? path
: first(node.children || [], n => _locate(n, id, path.concat(n)));
res = locate(data, '102').map(n => n.name)
console.log(res);
Suppose I have an array of objects:
var a = [
{id: "1", "name": "ABC"},
{id: "2", "name": "XYZ"},
{id: "3", "name": "PQR"},
{id: "4", "name": "JKL"}
];
I need to find the index of object which has the key "id: '3' " in it. Is there any way to find the index of the object using underscore library?
I have achieved this using for loop, but want to know an easier approach.
In ECMAScript 6 you can use findIndex:
[
{id: "1", "name": "ABC"},
{id: "2", "name": "XYZ"},
{id: "3", "name": "PQR"},
{id: "4", "name": "JKL"}
].findIndex(o => o.id === "3"); // 2
Similarly, underscore also has _.findIndex:
_.findIndex([
{id: "1", "name": "ABC"},
{id: "2", "name": "XYZ"},
{id: "3", "name": "PQR"},
{id: "4", "name": "JKL"}
], function(o){ return o.id === "3"}); // 2