Find nested object based on a parameter in Javascript - javascript

I have an array which looks like this:
[
{
"boxes": [
{
"id": 2,
"content": {
"name": "ABC",
"details": "some details for abc"
}
}
]
},
{
"boxes": [
{
"id": 3,
"content": {
"name": "XYZ",
"details": "some details for xyz"
}
},
{
"id": 4,
"content": {
"name": "UVW",
"details": "some details for uvw"
}
}
]
},
{}
]
And I have a variable: let id = 3
I want to be able to search through the nested array "boxes" to find the content property of the object that have the given id. Such that the result is:
{
"name": "XYZ",
"details": "some details for xyz"
}
Till now I have gathered that I can use a combination forEach and .filter do find this. But I'm not sure how. Also, I have control over the original data. So, if there's a better way to store the original data, I will be glad to have suggestions.
Actually I did attempt but got stuck:
Let's say the original array is called house.
let matches = []
let id = 3
house.forEach(function(e) {
matches = matches.concat(e.boxes.filter(function(b) {
return (b.id === id);
}));})
console.log(matches[0].content)

You could use map method by passing a callback function as argument. The scope of map method is to get all items from boxes array.
Also, I'm using filter(Boolean) statement in order to remove undefined value for those items which doesn't have boxes as property.
At least, use find method in order to get the desired output result.
let arr = [ { "boxes": [ { "id": 2, "content": { "name": "ABC", "details": "some details for abc" } } ] }, { "boxes": [ { "id": 3, "content": { "name": "XYZ", "details": "some details for xyz" } }, { "id": 4, "content": { "name": "UVW", "details": "some details for uvw" } } ] }, {} ]
let id = 1;
let result = [].concat(...arr.map(item => item.boxes))
.filter(Boolean)
.find(({id}) => id == id).content;
console.log(result);

First you need to put boxes object into one array, it seems it just has couple extra levels you don't need; You can create a helper function for that.
make a loop with if statement if there is what you are looking for or not

Here I use forEach() to loop through the data and get the boxes.
After that, I use filter() to get the object with the desired id. That's all.
const data = [
{
"boxes": [
{
"id": 2,
"content": {
"name": "ABC",
"details": "some details for abc"
}
}
]
},
{
"boxes": [
{
"id": 3,
"content": {
"name": "XYZ",
"details": "some details for xyz"
}
},
{
"id": 4,
"content": {
"name": "UVW",
"details": "some details for uvw"
}
}
]
},
{}
]
data.forEach(data => {
if (data.boxes){
let searchedData = data.boxes.filter(box => box.id == 3);
if (searchedData.length > 0){
console.log(searchedData[0].content);
}
}
});

Related

Replacing Data in Array

I am working on an angular application. I have an array as follows:
[{
"Name": "Andy"
},
{
"Name": "Bayer"
},
{
"Name": "James"
},
{
"Name": "Doda"
}]
I have another array which containes data as follows:
[
{
"Name": "Andy",
"Id": "1",
"Time": "2020-06-19T11:02+00:00"
},
{
"Name": "Billy",
"Id": "2",
"Time": "2020-06-19T11:05+00:00"
},
{
"Name": "Ciena",
"Id": 5
"Time": "2020-06-19T11:05+00:00"
},
{
"Name": "Doda",
"Id": "4",
"Time": "2020-06-19T11:05+00:00"
}
]
I want a resultant array such that code should check if Name is present in first array, then it should copy data from second array for that Name and push it in resultant array. For example common name between above two array is Andy and Doda, so data from Andy and Doda should be pushed to resultant array as follows:
[{
"Name": "Andy",
"Id": "1",
"Time": "2020-06-19T11:02+00:00"
},
{
"Name": "Bayer"
},
{
"Name": "James"
},
{
"Name": "Doda",
"Id": "4",
"Time": "2020-06-19T11:05+00:00"
}]
At run time I may get many names so code should be generic. I was trying following code which I got over stackoverflow itself
this.newArray = _.map(this.resultantArray, item => {
const value = _.find(this.dataArray, ['Name', item]);
const obj = value ? value : {Name: item};
return obj;
});
But this code is not working as expected as it works fine for the first time but when data comes for second time it appends data to previous data. I want array to be populated again freshly every time I send data. Please help
You can do this with vanilla JS no need for lodash. You can first map it and inside that you can find the value from second array otherwise return the current object:
var arrayTwo = [ { "Name": "Andy", "Id": "1", "Time": "2020-06-19T11:02+00:00" }, { "Name": "Billy", "Id": "2", "Time": "2020-06-19T11:05+00:00" }, { "Name": "Ciena", "Id": "5", "Time": "2020-06-19T11:05+00:00" }, { "Name": "Doda", "Id": "4", "Time": "2020-06-19T11:05+00:00" } ];
var arrayOne = [{ "Name": "Andy"}, { "Name": "Bayer"}, { "Name": "James"}, { "Name": "Doda"}];
var result = arrayOne.map(val=>arrayTwo.find(p=>p.Name==val.Name) || val);
console.log(result);
Suppose first array name is First
First : any [] = [{"Name": "Andy"},{"Name": "Bayer"},{ "Name": "James"},{"Name": "Doda"}]
And Second array name is Second
Second : any[] = [{"Name": "Andy","Id": "1","Time": "2020-06-19T11:02+00:00"},{"Name": "Bayer"},{"Name": "James"},{"Name": "Doda","Id": "4","Time": "2020-06-19T11:05+00:00"}]
Now do looping and check each name of first if its exists in second copy from second and push in result array
result : any[] =[];
this.First.forEach((element) => {
let index = this.Second.findIndex((x) => element.Name== x.Name);
if (index > -1) {
let data = {
this.Second[index].Name,
this.Second[index].Id,
this.Second[index].time,
};
this.result.push(data);
}
}

How can I insert an object in an array of objects

I want to convert my object status field that is would be modified specifically.
I have found the answer but no fill my goal that's why I updated my question
I have objects like this below:
Items = [
{
"id": 9,
"alias": "5cbe5c1c-e36b-422d-beb3-225a8e549bf1",
"name": "sfasf",
"status": 1
},
{
"id": 5,
"alias": "ed8a6921-c2c2-4a49-8893-5bf5c2bc0d98",
"name": "Test",
"status": 2
},
{
"id": 6,
"alias": "ed8a921-c2c2-4a49-8893-5bf5c2bc0d98",
"name": "Test",
"status": 3
}
]
I need to convert my object like below or I need to print like belows:
[
{
"id": 9,
"alias": "5cbe5c1c-e36b-422d-beb3-225a8e549bf1",
"name": "sfasf",
"status": {
"1": "ACTIVE"
}
},
{
"id": 5,
"alias": "ed8a6921-c2c2-4a49-8893-5bf5c2bc0d98",
"name": "Test",
"status": {
"2": "INACTIVE"
}
},
{
"id": 6,
"alias": "ed8a921-c2c2-4a49-8893-5bf5c2bc0d98",
"name": "Test",
"status": {
"3": "DELETED"
}
}
]
For example:
const possibleStatus = {
1: 'ACTIVE',
2: 'INACTIVE'
}
Items.map(item => ({...item, status: {[item.status]: possibleStatus[item.status]}}))
Update: addded lookup via possibleStatus
If nullish coalescing operator ?? is available, I would add a fallback for undefined status:
Items.map(item => ({...item, status: {[item.status]: possibleStatus[item.status] ?? `UNDEFINED STATUS ${item.status}`}}))
but only if this is display logic. In case of business logic, I'd rather check for valid values and throw an exception, e.g. encapsulated in a function mapping the status string to the object.
let statusTable = {
1: "ACTIVE",
2: "INACTIVE",
3: "DELETED"
}
let Items = [
{
"id": 9,
"alias": "5cbe5c1c-e36b-422d-beb3-225a8e549bf1",
"name": "sfasf",
"status": 1
},
{
"id": 5,
"alias": "ed8a6921-c2c2-4a49-8893-5bf5c2bc0d98",
"name": "Test",
"status": 2
},
{
"id": 6,
"alias": "ed8a921-c2c2-4a49-8893-5bf5c2bc0d98",
"name": "Test",
"status": 3
}
]
let result = Items.map(el => {
el.status = { [el.status]: statusTable[el.status] }
return el;
})
console.log(result);
I hope this solution be useful for you.
Items = [
{
"id": 9,
"alias": "5cbe5c1c-e36b-422d-beb3-225a8e549bf1",
"name": "sfasf",
"status": 1
},
{
"id": 5,
"alias": "ed8a6921-c2c2-4a49-8893-5bf5c2bc0d98",
"name": "Test",
"status": 2
}
]
Items = Items.map(item => {
item.status = item.status == 1 ? { "1": "ACTIVE" } : { "2": "INACTIVE" }
return item;
} )
console.log(Items);
If this is json, first you might want to parse it with JSON.parse, like following:
let parse = JSON.parse(yourJsonObj)
Next you get your array, which you need to modify. You can use map method and return a new array with the data you need:
let newData = parse.map(item => {
item.status = { [item.status]: "INACTIVE" };
return item;
});
Then you can go back and stringify it back if needed with JSON.stringify(newData).
The rules by which you set INACTIVE or ACTIVE I don't know, but this is the gist of it.
As others have indicated, map() is the way to go.
I've stepped things out a little here in such a way that the system wouldn't have unintended consequences if a third property was introduced. The switch statement explicitly only changes things if the status is 1 or 2 and leaves things alone otherwise.
The other examples given are probably fine for your use case though.
var items = [ { "id": 9, "alias": "5cbe5c1c-e36b-422d-beb3-225a8e549bf1", "name": "sfasf", "status": 1 }, { "id": 5, "alias": "ed8a6921-c2c2-4a49-8893-5bf5c2bc0d98", "name": "Test", "status": 2 },
{
"id": 6,
"alias": "ed8a921-c2c2-4a49-8893-5bf5c2bc0d98",
"name": "Test",
"status": 3
} ];
const process = () => {
// render original
const orig = document.querySelector('.original');
orig.innerHTML = '';
items.forEach(i => orig.innerHTML += `<li>${i.status}</li>`);
var result = items.map(i => {
switch(i.status) {
case(1):
i.status = {"1": "ACTIVE"}
break;
case(2):
i.status = {"2": "INACTIVE"}
break;
case(3):
i.status = {"3": "DELETED"}
break;
default:
// if status is not 1, 2 or 3, do nothing to the object
break;
}
// return the object
return i
})
// render processed
const res = document.querySelector('.results');
res.innerHTML = '';
items.forEach(i => res.innerHTML +=
`<li>${JSON.stringify(i.status)}</li>`);
}
process()
<div class="cols">
<div>
<p>Original</p>
<ul class="original">
</ul>
</div>
<div>
<p>Result</p>
<ul class="results"></ul>
</div>

Lodash to filter out an array of objects

I am trying to filter out a nested array of objects using lodash which is pretty straightforward but I am looking to avoid multiple calls.
I am looking to create 2 array of objects using a single lodash call/function. Looking to find object property "$isMultiAccount" if it exists put the whole object into one result set and if not put it to another ruleset.
Currently I am doing this with Lodash "has and filter" for first and for other "!has" which means same object is looped twice , as object is relatively large its creating bottleneck for speed
https://repl.it/repls/HomelyExpensiveTruetype
const item = {
"domains": [
{
"id": "dm11022",
"information":{
"description": "Customer",
"owner": {
"primary":{
"name": "James",
"phone": "NA"
},
"others": [
{
"$isMultiAccount": "./Yes"
}
]
}
}
},
{
"id": "dm12022",
"information":{
"description": "Customer",
"owner": {
"primary":{
"name": "James",
"phone": "NA"
},
"others": [
{
"$isMultiAccount": "./No"
}
]
}
}
},
{
"id": "dm12022",
"information":{
"description": "Customer",
"owner": {
"primary":{
"name": "James",
"phone": "NA"
},
"others": [
{
"conf": {
"isVpnBased":{
"accountType": "Primary"
}
}
}
]
}
}
}
]
}
/*
Expected result
output1 = [
{
"id": "dm11022",
"information":{
"description": "Customer",
"owner": {
"primary":{
"name": "James",
"phone": "NA"
},
"others": [
{
"$isMultiAccount": "./Yes"
}
]
}
}
},
{
"id": "dm12022",
"information":{
"description": "Customer",
"owner": {
"primary":{
"name": "James",
"phone": "NA"
},
"others": [
{
"$isMultiAccount": "./No"
}
]
}
}
}
]
// $isMultiAccount account do not exist in this object
output2 = [
{
"id": "dm12022",
"information":{
"description": "Customer",
"owner": {
"primary":{
"name": "James",
"phone": "NA"
},
"others": [
{
"conf": {
"isVpnBased":{
"accountType": "Primary"
}
}
}
]
}
}
}
]
*/
const item = {
"domains": [
{
"id": "dm11022",
"information":{
"description": "Customer",
"owner": {
"primary":{
"name": "James",
"phone": "NA"
},
"others": [
{
"$isMultiAccount": "./Yes"
}
]
}
}
},
{
"id": "dm12022",
"information":{
"description": "Customer",
"owner": {
"primary":{
"name": "James",
"phone": "NA"
},
"others": [
{
"$isMultiAccount": "./No"
}
]
}
}
},
{
"id": "dm12022",
"information":{
"description": "Customer",
"owner": {
"primary":{
"name": "James",
"phone": "NA"
},
"others": [
{
"conf": {
"isVpnBased":{
"accountType": "Primary"
}
}
}
]
}
}
}
]
}
const [areMulti, areNotMulti] = _.reduce(item.domains, (current, next) => {
return _.has(next, ‘information.owner.others.$isMultiAccount’)
? [current[0].concat(next), current[1]]
: [current[0], current[1].concat(next)];
}, [[], []]);
console.log(areMulti);
console.log(areNotMulti);
Since item.domains.information.owner.others is an array, you need to tackle it as follows:
let multi = [];
let notMulti = [];
_.each(item.domains, function (obj) {
if (obj.information.owner.others.length && _.has(obj.information.owner.others[0], '$isMultiAccount'))
multi.push(obj);
else
notMulti.push(obj);
});
console.log(multi);
console.log(notMulti);
Unfortunately, you have to iterate over the domains array as well ass on the owner.others array to determine if the object with specific key sits inside.
So the algorithm has O(n*m) complexity.
If you ask for a lodash function seems that the partition method is what you're looking for
As docs says:
Creates an array of elements split into two groups, the first of which contains elements predicate returns truthy for, the second of which contains elements predicate returns falsey for. The predicate is invoked with one argument: (value).
So it will be like:
_.partition(
item.domains,
e => _.some(
_.get(e, 'information.owner.others'),
el => _.has(el,"$isMultiAccount")
)
);
Watch out - some hack available!
However, if the you're 100% sure that the element you're looking for will be always at specific index (for example it is supposed to be always as first element - so index 0) you can limit the algorithm to have linear complexity O(n) as only the size of the domains array will matter in terms of performance.
The hackish solution assuming fixed array index=0:
_.partition(
item.domains,
e => _.has(e, 'information.owner.others.0.$isMultiAccount')
);
NOTE
Using lodash makes code a bit easier to read but of course it creates some performance overhead anyway.

Iterate through a json with hierarchical children and add a key value pair

I want to convert the existing data to other. Please find below the existing code and the expected code.
Existing:
{
"title": "title1",
"child": [
{
"title": "Header",
"child":
[
{
"title": "test",
"child":
[
{
"title": "testchild",
},
{
"title": "Descriptionchild",
}
]
},
{
"title": "Description",
}
]
}
]
}
Expected:
{
"title": "title1",
"customId": "title1-xx"
"child": [
{
"title": "Header",
"customId": "Header1-xx",
"child":
[
{
"title": "test",
"customId": "test1-xx",
"child":
[
{
"title": "testchild",
"customId": "testchild1-xx"
},
{
"title": "Descriptionchild",
"customId": "Descriptionchild1-xx"
}
]
},
{
"title": "Description",
"customId": "Description1-xx"
}
]
}
]
}
We can define a function that takes an array and recursively add this attribute:
// input must be array
function recursivelyAddCustomId(input) {
// if input is not empty i.e. child is not empty
if (input != null) {
// for each child object in array
for (let obj of input) {
// set custom id
if (obj.title != null) {
obj.customId = obj.title + '-xx';
}
// recurse (doesn't matter if child exists)
recursivelyAddCustomId(obj.child);
}
}
}
// put input as array
recursivelyAddCustomId([input]);
console.log(input);
Note that the input for this function must be an array so the first object must be converted to an array first.
Please let me know if there is anything I need to clarify.
Note: Comments are made in code block

Get parent object using jsonpath query

i have the json below:
{
"data": [
{
"name": "product1",
"details": ["lorem ipsum", [
{
"code": "prd1"
},
{
"code": "prd11"
}]
]
},
{
"name": "product2",
"details": ["lorem ipsum", [
{
"code": "prd2"
},
{
"code": "prd22"
}]
]
}
]
}
and i want to retrieve the name of the product based on the code, so
i wrote this query
$.data..[?(#.code=="prd1")]
Result:
[
{
"code": "prd1"
}]
Expected result:
[{name: "product1"}]
You should move up the tree until you reach the value of a specific key. At first, we need to reach the first named ancestor, which is details, then - to get its first parent, which holds the needed name property. This can be done using ancestor() function and parent() function:
$.data..[?(#.code=="prd1")].ansector("details").parent()['name'];

Categories

Resources