Lets say if we have a deeply nested comment of comments.
const comments = [
{
id: 1,
text: "Comment 1",
comments: [
{
id: 11,
text: "Comment 1a",
comments: [
{
id: 111,
text: "Comment 11a",
comments: [],
},
{
id: 112,
text: "Comment 11b",
comments: [],
},
{
id: 113,
text: "Comment 11c",
comments: [],
},
],
},
{
id: 12,
text: "Comment 12",
comments: [
{
id: 121,
text: "Comment 12a",
comments: [],
},
{
id: 122,
text: "Comment 12b",
comments: [],
},
{
id: 123,
text: "Comment 12c",
comments: [],
},
],
},
{
id: 13,
text: "Comment 1c",
comments: [
{
id: 124,
text: "Comment 13a",
comments: [],
},
{
id: 125,
text: "Comment 13b",
comments: [],
},
{
id: 126,
text: "Comment 13c",
comments: [],
},
],
},
],
},
{
id: 2,
text: "Comment 2",
comments: [
{
id: 21,
text: "Comment 2a",
comments: [
{
id: 127,
text: "Comment 21a",
comments: [],
},
{
id: 128,
text: "Comment 21b",
comments: [],
},
{
id: 129,
text: "Comment 21c",
comments: [
{
id: 130,
text: "Comment 21cc",
comments: [
{
id: 131,
text: "Comment 21ccc",
comments: [],
},
],
},
],
},
],
},
{
id: 22,
text: "Comment 2b",
comments: [
{
id: 135,
text: "Comment 21a",
comments: [],
},
{
id: 132,
text: "Comment 21b",
comments: [],
},
{
id: 133,
text: "Comment 21c",
comments: [],
},
],
},
{
id: 23,
text: "Comment 2c",
comments: [],
},
],
},
{
id: 3,
text: "Comment 3",
comments: [
{
id: 31,
text: "Comment 3a",
comments: [],
},
{
id: 32,
text: "Comment 3b",
comments: [],
},
{
id: 33,
text: "Comment 3c",
comments: [],
},
],
},
];
How would we find a deeply nested comment such as comment with the id of 131?
I tried the following and it works.
function recursiveFind(comments, commentId) {
const result = [];
function loop(comments, commentId, result) {
for (const comment of comments) {
if (comment.id === commentId) {
result.push(comment);
}
if (result.length <= 0 && comment.comments) {
loop(comment.comments, commentId, result);
}
}
}
loop(comments, commentId, result);
return result.length > 0 ? result[0] : false;
}
const found = recursiveFind(comments, 131);
console.log(found);//returns object
Is there a more elegant way to do this ? I know another option would be flattening the array and then returning the object?
However, is there a better way to do this ?
Thank you.
Edit: Yes ID is unique, I have corrected the id.
We already have a free tree walker in JS - JSON.stringify:
function find(data, fn) {
let found = []
JSON.stringify(data, (key, val) => {
if (fn(val))
found.push(val)
return val
})
return found
}
and then simply:
results = find(comments, x => x.id === 131)
You can use a .reduce() and a recursion:
const comments = [ { id: 1, text: "Comment 1", comments: [ { id: 11, text: "Comment 1a", comments: [ { id: 111, text: "Comment 11a", comments: [], }, { id: 112, text: "Comment 11b", comments: [], }, { id: 113, text: "Comment 11c", comments: [], }, ], }, { id: 12, text: "Comment 12", comments: [ { id: 121, text: "Comment 12a", comments: [], }, { id: 122, text: "Comment 12b", comments: [], }, { id: 123, text: "Comment 12c", comments: [], }, ], }, { id: 13, text: "Comment 1c", comments: [ { id: 124, text: "Comment 13a", comments: [], }, { id: 125, text: "Comment 13b", comments: [], }, { id: 126, text: "Comment 13c", comments: [], }, ], }, ], }, { id: 2, text: "Comment 2", comments: [ { id: 21, text: "Comment 2a", comments: [ { id: 127, text: "Comment 21a", comments: [], }, { id: 128, text: "Comment 21b", comments: [], }, { id: 129, text: "Comment 21c", comments: [ { id: 130, text: "Comment 21cc", comments: [ { id: 131, text: "Comment 21ccc", comments: [], }, ], }, ], }, ], }, { id: 22, text: "Comment 2b", comments: [ { id: 130, text: "Comment 21a", comments: [], }, { id: 132, text: "Comment 21b", comments: [], }, { id: 133, text: "Comment 21c", comments: [], }, ], }, { id: 23, text: "Comment 2c", comments: [], }, ], }, { id: 3, text: "Comment 3", comments: [ { id: 31, text: "Comment 3a", comments: [], }, { id: 32, text: "Comment 3b", comments: [], }, { id: 33, text: "Comment 3c", comments: [], }, ], }, ];
function recursiveFind(comments, commentId) {
const result = comments.reduce((acc, obj) => {
if(obj.id === commentId) {
acc = obj;
} else if(obj.comments.length) {
const found = recursiveFind(obj.comments, commentId);
if(found) acc = found;
}
return acc;
}, null);
return result;
}
console.log('search id 131:', recursiveFind(comments, 131));
Output:
{
"id": 131,
"text": "Comment 21ccc",
"comments": []
}
Docs: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
UPDATE 1 to address feedback that IDs are not unique. I also remove nested comments; if thatit desired, remove the .map(obj => ({ id: obj.id, text: obj.text }))
UPDATE 2 reverted update 1 since IDs are actually unique.
Related
I have a 2D array where the outer array contains objects, each of which contains another array of objects. Here is the data:
const companies = [{
companyId: 100,
companyName: "Company 1",
transactions: [
{ id: "10421", date: "09/19/2022", selected: true, },
{ id: "00467", date: "05/08/2022", selected: true, },
],
},
{
companyId: 200,
companyName: "Company 2",
transactions: [
{ id: "259A6", date: "01/11/2022", selected: false, },
{ id: "87K37", date: "04/14/2022", selected: false, },
],
},
];
I want to get the list of transactions that have their selected fields set to true. This is how I am filtering the data:
const selectedTransactions = companies.filter((c) =>
c.transactions.filter((t) => t.selected === true));
Instead of getting the transactions of only the first company, I'm getting the transactions of ALL companies, including the ones that are not selected. Am I making a mistake somewhere?
You could flat-map the overall companies; then internally filter and map the transaction IDs and dates along with the company ID.
const companies = [{
companyId: 100,
companyName: "Company 1",
transactions: [
{ id: "10421", date: "09/19/2022", selected: true },
{ id: "00467", date: "05/08/2022", selected: true },
],
}, {
companyId: 200,
companyName: "Company 2",
transactions: [
{ id: "259A6", date: "01/11/2022", selected: false },
{ id: "87K37", date: "04/14/2022", selected: false },
],
}];
const selected = companies
.flatMap(({ companyId, transactions }) => transactions
.filter(({ selected }) => selected)
.map(({ id: transactionId, date: transactionDate }) =>
({ companyId, transactionId, transactionDate })));
console.log(selected);
.as-console-wrapper { top: 0; max-height: 100% !important; }
Result
[
{ "companyId": 100, "transactionId": "10421", "transactionDate": "09/19/2022" },
{ "companyId": 100, "transactionId": "00467", "transactionDate": "05/08/2022" }
]
If youre just looking for the transactions you could use map, though it does return a second empty array. Or just add a little to your filter function to get only the company with what youre looking for.
const companies = [{
companyId: 100,
companyName: "Company 1",
transactions: [
{ id: "10421", date: "09/19/2022", selected: true },
{ id: "00467", date: "05/08/2022", selected: true },
],
}, {
companyId: 200,
companyName: "Company 2",
transactions: [
{ id: "259A6", date: "01/11/2022", selected: false },
{ id: "87K37", date: "04/14/2022", selected: false },
],
}];
// USING MAP TO GET TRANSACTIONS
const st = companies.map(c => c.transactions.filter(t => t.selected === true))
console.log(st);
// USING FILTER TO GET COMPANY
const selectedTransactions = companies.filter(c => {
c.transactions = c.transactions.filter(t => t.selected === true);
if (c.transactions.length) return c;
});
console.log(selectedTransactions);
.as-console-wrapper { top: 0; max-height: 100% !important; }
Result
1
[
[
{
"id": "10421",
"date": "09/19/2022",
"selected": true
},
{
"id": "00467",
"date": "05/08/2022",
"selected": true
}
],
[]
]
2
[
{
"companyId": 100,
"companyName": "Company 1",
"transactions": [
{
"id": "10421",
"date": "09/19/2022",
"selected": true
},
{
"id": "00467",
"date": "05/08/2022",
"selected": true
}
]
}
]
I can't figure out how to properly use .filter() to find an object in an Array by searching for a value in its nested Array.
I have the following data:
const orders = [
{
id: 1,
items: [
{
itemId: "abc",
},
{
itemId: "def",
},
],
},
{
id: 2,
items: [
{
itemId: "jkl",
},
{
itemId: "mno",
},
],
},
{
id: 3,
items: [
{
itemId: "abc",
},
{
itemId: "xyz",
},
],
},
];
I have the needed itemId: "abc" which I need to use to find all the objects that have items that also have the Id of "abc", so that the result is this:
[
{
id: 1,
items: [
{
itemId: "abc",
},
{
itemId: "def",
},
],
},
{
id: 3,
items: [
{
itemId: "abc",
},
{
itemId: "xyz",
},
],
},
]
So far, I've tried the following:
orders &&
orders.filter((order) => {
return order.items.filter((item) => item.itemId === "abc");
});
But it doesn't seem to work. What am I missing here?
Chris G beat me to it in the comments but he is right, you need to use order.items.some in your inner function:
const orders = [{
id: 1,
items: [{
itemId: "abc",
},
{
itemId: "def",
},
],
},
{
id: 2,
items: [{
itemId: "jkl",
},
{
itemId: "mno",
},
],
},
{
id: 3,
items: [{
itemId: "abc",
},
{
itemId: "xyz",
},
],
},
]
var ans = orders.filter((order) => {
return order.items.some((item) => item.itemId === "abc");
});
console.log(ans)
const orders = [{
id: 1,
items: [{
itemId: "abc",
},
{
itemId: "def",
},
],
},
{
id: 2,
items: [{
itemId: "jkl",
},
{
itemId: "mno",
},
],
},
{
id: 3,
items: [{
itemId: "abc",
},
{
itemId: "xyz",
},
],
},
];
const result = orders.filter(order =>
order.items.find(item => item.itemId === 'abc') !== undefined
)
console.log(result);
I have an array of objects with objects and children and children can have elements
let array = {
account: "account 1",
children: [
{
account: "1",
},
{
account: "2",
},
],
total: {
debit: 96,
credit:96,
},
},
{
account: "account 2",
children: [
{
account: "1",
},
],
total: {
debit: 45,
credit: 96,
},
}
]
The expected array is to push an object to each of the children here and the object will be the total object we have at the bottom.
Expected Result:
let resultArray = {
account: "account 1",
children: [
{
account: "1",
},
{
account: "2",
},
{
debit: 96,
credit:96,
},
],
total: {
debit: 96,
credit:96,
},
},
{
account: "account 2",
children: [
{
account: "1",
},
{
debit: 45,
credit: 96,
},
],
total: {
debit: 45,
credit: 96,
},
},
Any suggestions for the same?
var resultArray = array.map((data) => {
const newChildrenArray = [...data.children];
newChildrenArray.push({...data.total});
return { ...data, children: newChildrenArray };
});
Note:
Your array and resultArray are not valid.
They should be like:
let array = [obj1, obj2]
Hope this will solve your problem!
I need a typescript function to recursively get the full path (parent hierarchy) of a given node by passing its value.
Let's say I have an array of objects like this:
items = [{
value: 2,
text: "Name 2",
children: [{
value: 7,
text: "Name 7",
children: [{
value: 10,
text: "Name 10",
children: []
},
{
value: 11,
text: "Name 11",
children: []
},
{
value: 12,
text: "Name 12",
children: [{
value: 13,
text: "Name 13",
children: [{
value: 14,
text: "Name 14",
children: []
},
{
value: 15,
text: "Name 15",
children: []
}
]
}]
}
]
}]
},
{
value: 16,
text: "Name 16",
children: [{
value: 17,
text: "Name 17",
children: [{
value: 18,
text: "Name 18",
children: []
},
{
value: 19,
text: "Name 19",
children: []
}
]
}]
}
];
Let's say I want to get the full path of node with value=19 by calling a function.
getPath(items, 19);
The expected result could be either returning only values of parent nodes
[16, 17, 19]
or array of objects as bellow:
[{
value: 16,
text: "Name 16"
},
{
value: 17,
text: "Name 17"
},
{
value: 19,
text: "Name 19"
}
]
Thanks,
Hope this help
const items = [{
value: 2,
text: "Name 2",
children: [{
value: 7,
text: "Name 7",
children: [{
value: 10,
text: "Name 10",
children: []
},
{
value: 11,
text: "Name 11",
children: []
},
{
value: 12,
text: "Name 12",
children: [{
value: 13,
text: "Name 13",
children: [{
value: 14,
text: "Name 14",
children: []
},
{
value: 15,
text: "Name 15",
children: []
}
]
}]
}
]
}]
},
{
value: 16,
text: "Name 16",
children: [{
value: 17,
text: "Name 17",
children: [{
value: 18,
text: "Name 18",
children: []
},
{
value: 19,
text: "Name 19",
children: []
}
]
}]
}
];
function getPath(items, val) {
for (let i = 0; i < items.length; i++) {
const item = items[i];
if (item.value !== val) {
if (item.children) {
const path = getPath(item.children, val);
if (path) {
path.unshift(item.value);
return path;
}
}
} else {
return [item.value];
}
}
}
console.log(getPath(items, 19));
Here's the link for it
I have a complex JavaScript object given below.
An example object:
var object= {
"name": "tfifkhul",
"id": "262761",
"children": [
{
"name": "rthrth",
"id": 0,
"children": [
{
"name": "test",
"id": "262762",
"children": []
}
]
},
{
"name": "rthsrth",
"id": 0,
"children": [
{
"name": "test",
"id": "262762",
"children": []
}
]
},
{
"name": "rthrthhrth",
"id": 0,
"children": [
{
"name": "test",
"id": "262762",
"children": [
{
"name": "rtjrtj",
"id": 0,
"children": [
{
"name": "fwefwefwef",
"id": "262768",
"children": []
}
]
},
{
"name": "hsrtjrtdjrtj",
"id": 0,
"children": [
{
"name": "we4yhesrhy",
"id": "262764",
"children": []
}
]
},
{
"name": "lol",
"id": "262763",
"children": [
{
"name": "fwefwefwef",
"id": "262768",
"children": [
{
"name": "87ok78",
"id": "262765",
"children": [
{
"name": "78o78",
"id": 0,
"children": [
{
"name": "we4yhesrhy",
"id": "262764",
"children": [
{
"name": "test1",
"id": 0,
"children": [
{
"name": "",
"id": "262766",
"children": []
}
]
},
{
"name": "test2",
"id": 0,
"children": [
{
"name": "",
"id": "262766",
"children": []
}
]
}
]
}
]
},
{
"name": "7o78o76o8",
"id": 0,
"children": [
{
"name": "",
"id": "262766",
"children": []
}
]
},
{
"name": "ko",
"id": 0,
"children": [
{
"name": "",
"id": "262767",
"children": []
}
]
}
]
}
]
}
]
}
]
}
]
}
]
};
I need to create a function to search for all matching values for key "id" with given value.
So far I have created one recursive function:
function searchOccurances(theObject, value,path) {
var result = null;
if(theObject instanceof Array) {
for(var i = 0; i < theObject.length; i++) {
result = searchOccurances(theObject[i],value,path+","+i);
}
}
else
{
for(prop in theObject) {
if(prop == 'id') {
if(theObject[prop] == value) {
keyOccurances.push(path);
}
}
if((theObject[prop] instanceof Array) || (theObject[prop] instanceof Object))
{
if((theObject[prop].length!=undefined)&&(theObject[prop].length!=0))
{
result = searchOccurances(theObject[prop],value,path+","+prop);
}
}
}
}
return result;
}
keyOccurances=[];
searchOccurances(object,262762,'');
console.log(keyOccurances);
//Output
[",children,0,children,0", ",children,1,children,0", ",children,2,children,0"] -- correct
keyOccurances=[];
searchOccurances(object,262768,'');
console.log(keyOccurances);
//Output
[",children,1,children,0,children,1,children,0", ",children,1,children,0,children,2,children,0"] --wrong
The function returns array of comma separated paths of matched value but doesn't seems to be getting right results. For the first call with value '262762' gives corrects path list but for value '262768' gives incorrect path list.
Kindly help.
I'd suggest to provide a better test object. Would you really have so many children with 'id = 0' in a real use case? Would you have 2 children with the same ID at all? That makes things pretty hard to debug.
Below is an example function that should work as expected.
function search(object, value) {
var res = [], searchPath;
(searchPath = function(children, path) {
var n, newPath;
for(n in children) {
if(typeof children[n].id !== 'undefined' && parseInt(children[n].id, 10) === value) {
res.push(path);
}
newPath = path.slice();
newPath.push(children[n].id);
searchPath(children[n].children, newPath);
}
})([ object ], []);
return res;
}
console.log(search(object, 262762));
console.log(search(object, 262768));
Output:
[["262761", 0], ["262761", 0], ["262761", 0]]
[["262761", 0, "262762", 0], ["262761", 0, "262762", "262763"]]
The above code is not (yet) bullet-proof but hopefully is it short enough to be easily understandable.
If I understand your questions correctly, you're looking for all paths where a specific id is present. I'd recommend not reinventing the wheel here and using an existing library. We use object-scan for most of our data processing now. It's powerful once you wrap your head around it. Here is how you'd answer your question
// const objectScan = require('object-scan');
const findKeys = (haystack, id) => objectScan(['**'], {
joined: true,
filterFn: ({ value }) => value.id === id
})(haystack);
const object = { name: 'tfifkhul', id: '262761', children: [{ name: 'rthrth', id: 0, children: [{ name: 'test', id: '262762', children: [] }] }, { name: 'rthsrth', id: 0, children: [{ name: 'test', id: '262762', children: [] }] }, { name: 'rthrthhrth', id: 0, children: [{ name: 'test', id: '262762', children: [{ name: 'rtjrtj', id: 0, children: [{ name: 'fwefwefwef', id: '262768', children: [] }] }, { name: 'hsrtjrtdjrtj', id: 0, children: [{ name: 'we4yhesrhy', id: '262764', children: [] }] }, { name: 'lol', id: '262763', children: [{ name: 'fwefwefwef', id: '262768', children: [{ name: '87ok78', id: '262765', children: [{ name: '78o78', id: 0, children: [{ name: 'we4yhesrhy', id: '262764', children: [{ name: 'test1', id: 0, children: [{ name: '', id: '262766', children: [] }] }, { name: 'test2', id: 0, children: [{ name: '', id: '262766', children: [] }] }] }] }, { name: '7o78o76o8', id: 0, children: [{ name: '', id: '262766', children: [] }] }, { name: 'ko', id: 0, children: [{ name: '', id: '262767', children: [] }] }] }] }] }] }] }] };
console.log(findKeys(object, '262762'));
/* =>
[ 'children[2].children[0]',
'children[1].children[0]',
'children[0].children[0]' ]
*/
console.log(findKeys(object, '262768'));
/* =>
[ 'children[2].children[0].children[2].children[0]',
'children[2].children[0].children[0].children[0]' ]
*/
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
Disclaimer: I'm the author of object-scan
Edit: The accepted answer didn't make much sense to me (based on the question), but here is how you could generate the same output
// const objectScan = require('object-scan');
const findIdPath = (haystack, id) => objectScan(['**'], {
reverse: false,
filterFn: ({ value, parents, context }) => {
if (value.id === id) {
context.push(parents.filter((p) => 'id' in p).map((p) => p.id).reverse());
}
}
})(haystack, []);
const object = { name: 'tfifkhul', id: '262761', children: [{ name: 'rthrth', id: 0, children: [{ name: 'test', id: '262762', children: [] }] }, { name: 'rthsrth', id: 0, children: [{ name: 'test', id: '262762', children: [] }] }, { name: 'rthrthhrth', id: 0, children: [{ name: 'test', id: '262762', children: [{ name: 'rtjrtj', id: 0, children: [{ name: 'fwefwefwef', id: '262768', children: [] }] }, { name: 'hsrtjrtdjrtj', id: 0, children: [{ name: 'we4yhesrhy', id: '262764', children: [] }] }, { name: 'lol', id: '262763', children: [{ name: 'fwefwefwef', id: '262768', children: [{ name: '87ok78', id: '262765', children: [{ name: '78o78', id: 0, children: [{ name: 'we4yhesrhy', id: '262764', children: [{ name: 'test1', id: 0, children: [{ name: '', id: '262766', children: [] }] }, { name: 'test2', id: 0, children: [{ name: '', id: '262766', children: [] }] }] }] }, { name: '7o78o76o8', id: 0, children: [{ name: '', id: '262766', children: [] }] }, { name: 'ko', id: 0, children: [{ name: '', id: '262767', children: [] }] }] }] }] }] }] }] };
console.log(findIdPath(object, '262762'));
// => [ [ '262761', 0 ], [ '262761', 0 ], [ '262761', 0 ] ]
console.log(findIdPath(object, '262768'));
// => [ [ '262761', 0, '262762', 0 ], [ '262761', 0, '262762', '262763' ] ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
Disclaimer: I'm the author of object-scan