How to make a function as pure function using javascript and react? - javascript

i have data like below,
const arr_obj = [
{
id: '1',
children: [],
type: 'TYPE1',
},
{
id: '2',
children: [
{
id: '1',
children: [
{
//some attributes
}
],
type: 'MAIN',
},
{
id: '2',
children: [
{
//some attributes
}
],
type: 'MAIN',
},
{
id: '3',
children: [
{
//some attributes
}
],
type: 'MAIN',
},
]
type: 'TYPE2',
},
{
id: '3',
children: [
{
id: '4',
children: [
{
//some attributes
}
],
type: 'MAIN',
},
{
id: '5',
children: [
{
//some attributes
}
],
type: 'MAIN',
},
{
id: '6',
children: [
{
//some attributes
}
],
type: 'MAIN',
},
]
type: 'TYPE2',
}
]
I have to find out the count of type: 'MAIN'. these 'MAIN' will be within type: "type2"
So the expected count is 6.
below is the code,
const ParentComponent = () => {
const findCount = (arr_obj) => {
let count = 0;
const expectedCount = 2;
const loop = (children) => {
for (const obj of children) {
const { type, children } = obj;
if (type === 'TYPE2') {
loop(children);
} else if (type === 'MAIN') {
++count;
if (count > expectedCount) return;
}
}
};
loop(children);
return count > expectedCount;
};
const output = findCount(arr_obj);
return (
//some jsx rendering
);
}
the above code works fine. but i want to make loop(children) function a pure function. I am not sure how to do it.
the problem now is i define variables outside the loop method.
how can i define everything as arguments to the function, you could move the function outside the component.
could someone help me with this. thanks.

You could take an array of the wanted type order and iterate only one level and han over the rest of wanted type. If no type are left over, return one otherwise the result of nested count.
const
getCount = (array, types) => {
let count = 0;
for (const { type, children } of array) {
if (types[0] === type) {
count += types.length === 1
? 1
: getCount(children, types.slice(1));
}
}
return count;
}
data = [{ id: '1', children: [], type: 'TYPE1' }, { id: '2', children: [{ id: '1', children: [{}], type: 'MAIN' }, { id: '2', children: [{}], type: 'MAIN' }, { id: '3', children: [{} ], type: 'MAIN' }], type: 'TYPE2' }, { id: '3', children: [{ id: '4', children: [{}], type: 'MAIN' }, { id: '5', children: [{}], type: 'MAIN' }, { id: '6', children: [{}], type: 'MAIN' }], type: 'TYPE2' }],
order = ['TYPE2', 'MAIN'],
count = getCount(data, order);
console.log(count);

Pure Function is a function (a block of code ) that always returns the same result if the same arguments are passed. It does not depend on any state, or data change during a program’s execution rather it only depends on its input arguments.
Reference
In the above shared code I could see expectedCount as the shared variable which is not the PURE function.
As I could see your comments the desired type is in Children then its just the 2 levels. Then the following code would work.
function count(data, condition) {
let count = 0;
data.forEach((value, index) => {
if(value.type === condition[0]){
value.children.forEach((val, idx) => {
if(val.type === condition[1]) {
count++;
}
})
}
});
return count;
}
const condition = ['TYPE2', 'MAIN'];
console.log(count(arr_obj, condition));

Nina's answer is more to the point but you could also do it by filtering the input array.
const data = [{ id: '1', children: [], type: 'TYPE1' }, { id: '2', children: [{ id: '1', children: [{}], type: 'MAIN' }, { id: '2', children: [{}], type: 'MAIN' }, { id: '3', children: [{} ], type: 'MAIN' }], type: 'TYPE2' }, { id: '3', children: [{ id: '4', children: [{}], type: 'MAIN' }, { id: '5', children: [{}], type: 'MAIN' }, { id: '6', children: [{}], type: 'MAIN' }], type: 'TYPE2' }];
const count = data
.filter(v=>v.type==='TYPE2')
.flatMap(v=>v.children)
.filter(v=>v.type==='MAIN')
.length
console.log(count);

Related

How to find the length of particular type within object using javascript?

i have object like below,
Example 1
input = {
item_type: {
id: ‘1’,
name: ‘name1’,
},
children: [
{
type_1: {
id: ‘12’,
},
type: 'item1-type',
},
{
children: [
{
id: '1',
type: 'item2',
},
{
id: '2',
type:'item2',
},
{
id:'3',
type: 'item2',
},
]
type: 'item2-type',
},
{
children: [
{
id: '4',
type: 'item2',
},
{
id: '5',
type:'item2',
},
{
id:'6',
type: 'item2',
},
]
type: 'item2-type',
},
]
}
now i want to find the count of "item2" type within children array within children array again.
note that the outer children array can be empty array and the children array within children array may not be present. so the input can be of types like below
input = {
item_type: {
id: ‘1’,
name: ‘name1’,
},
children: [] //empty children array
}
input = {
item_type: {
id: ‘1’,
name: ‘name1’,
},
children:
[ //no children array within
{
type_1: {
id: ‘12’,
},
type: “item1-type”,
},
]
}
how can i find the count of type: "item2" within children array considering example1 input.
so the expected count is 6.
could someone help me with this. thanks. new to programming.
const findAllChildrenOfType = (obj, type) => {
let count = 0;
if (obj.type === type) count++;
if (obj.children) {
obj.children.forEach(child => {
const childCount = findAllChildrenOfType(child, type);
count += childCount;
})
}
return count;
}
console.log(findAllChildrenOfType(input, "item2"))

Get the branch of collection without the sibling elements, searching by property

I have the object with the next structure:
let array = [
{
name: 'Name1',
items: [
{
name: 'Name1.1',
items: [
{ id: '1', name: 'Name1.1.1' },
{ id: '2', name: 'Name1.1.2' },
{ id: '3', name: 'Name1.1.3' },
...
],
},
{
name: 'Name1.2',
items: [
{ id: '4', name: 'Name1.2.1' },
{ id: '5', name: 'Name1.2.2' },
],
},
],
},
{
name: 'Name2',
items: [
{
name: 'Name2.1',
items: [
{ id: '6', name: 'Name2.1.1' },
{ id: '7', name: 'Name2.1.2' },
],
},
],
},
];
I want to get the branch without the sibling elements, searching by id. The desired result is the next structure by id = '4':
let array = [
{
name: 'Name1',
items: [
{
name: 'Name1.2',
items: [
{ id: '4', name: 'Name1.2.1' },
],
},
],
}
];
I could find only the end element of the tree ({ id: '4', name: 'Name1.2.1' }). But I don't understand how to get intermediate structures of the tree.
const test = (data, id) => {
if (!data || !data.length) return null;
for (var j = 0; j < data.length; j++) {
var result = data[j].items
? test(data[j].items, id)
: data[j].id
? data[j].id === id
? data[j]
: undefined
: undefined;
if (result !== undefined) {
return result;
}
}
return undefined;
};
test(array, '4');
You should indeed take a recursive approach, but your function currently can only return an id value (a string) or null or undefined. It never returns an array, yet that is what you expect to get.
When a solution is found as a base case, you need to wrap that solution in an array and plain object, each time you get out of the recursion tree.
Here is a working solution:
function getPath(forest, targetid) {
for (let root of forest) {
if (root.id === targetid) return [root]; // base case
let items = root.items && getPath(root.items, targetid);
if (items) return [{ ...root, items }]; // wrap!
}
}
// Example run:
let array = [{name: 'Name1',items: [{name: 'Name1.1',items: [{ id: '1', name: 'Name1.1.1' },{ id: '2', name: 'Name1.1.2' },{ id: '3', name: 'Name1.1.3' },],},{name: 'Name1.2',items: [{ id: '4', name: 'Name1.2.1' },{ id: '5', name: 'Name1.2.2' },],},],},{name: 'Name2',items: [{name: 'Name2.1',items: [{ id: '6', name: 'Name2.1.1' },{ id: '7', name: 'Name2.1.2' },],},],},];
console.log(getPath(array, '4'));

How to search a value from an json which has 2 level as return matches from both the object?

I have a array of object which has a inside array which need to be filtered and return array based on matches from both. search is (input) event, which executes on every key press.
stackblitz link stackblitz
list = [
{
id: 'abc',
data: [
{ key: '1', value: 'car' },
{ key: '2', value: 'bus' },
{ key: '3', value: 'bike' },
{ key: '4', value: 'truck' },
{ key: '5', value: 'jeep' },
],
},
{
id: 'def',
data: [
{ key: '1', value: 'car' },
{ key: '2', value: 'bicycle' },
{ key: '3', value: 'train' },
{ key: '4', value: 'aeroplane' },
{ key: '5', value: 'jeep' },
],
},
];
handleSearch = (event) => {
if (event.target.value.length > 0) {
const item = this.list[0].data.filter((items) =>
items.value.toLowerCase().includes(event.target.value.toLowerCase())
);
this.list[0].data = item;
} else {
this.list[0].data = this.orgList;
}
};
expect output
input = car
output = [
{
id: 'abc',
data: [
{ key: '1', value: 'car' },
],
},
{
id: 'def',
data: [
{ key: '1', value: 'car' },
],
},
];
input = truck
output =
[
{
id: 'abc',
data: [
{ key: '4', value: 'truck' },
],
},
];
const list = [{id: 'abc',data: [{ key: '1', value: 'car' },{ key: '2', value: 'bus' },{ key: '3', value: 'bike' },{ key: '4', value: 'truck' },{ key: '5', value: 'jeep' },],},{id: 'def',data: [{ key: '1', value: 'car' },{ key: '2', value: 'bicycle' },{ key: '3', value: 'train' },{ key: '4', value: 'aeroplane' },{ key: '5', value: 'jeep' },],},];
function search(arr, searchVal) {
return arr.map((item) => {
const data = item.data.filter(({ value }) => value === searchVal);
return { ...item, data };
})
.filter(({ data }) => data.length);
}
console.log(search(list, 'car'));
console.log(search(list, 'truck'));
.as-console-wrapper { max-height: 100% !important; top: 0 }
Angular demo
I know that I might be going a bit outside of the scope of your requirements here, but I just simply thought that it might be easier to do it like this.
I just thought that it might be somewhat more scalable this way, if you first flatten the structure, because for arguments sake, let's say that you're data structure needs to become more & more complex overtime, IDK, business requirements change. At least if you have some layer of abstraction to manage that, you can then filter on an array of objects quite simply, like I've done below.
Depending on your needs you may not even need to flatten the structure, it's just my opinion & experience states this to be an easier & more maintainable kinda solution to scale. If you're data structure dose evolve with complexity, where there may be nested structures, you could always look at using some clever little recursive function to flatten your structure.
It's worth also noting that I've added some validation to the search function, while it's probably not a requirement, it's not a bad idea to include such logic, where you could update state on your view model. You could include something like a toast notification, stating that the user has provided an invalid search term, you could be making a request to a server to get this data & you could say that there were no results, etc, I think you get the idea?
I hope that's helped & I'm sorry if I've gone a little OTT. 😅
const list = [
{
id: 'abc',
data: [
{ key: '1', value: 'car' },
{ key: '2', value: 'bus' },
{ key: '3', value: 'bike' },
{ key: '4', value: 'truck' },
{ key: '5', value: 'jeep' },
],
},
{
id: 'def',
data: [
{ key: '1', value: 'car' },
{ key: '2', value: 'bicycle' },
{ key: '3', value: 'train' },
{ key: '4', value: 'aeroplane' },
{ key: '5', value: 'jeep' },
],
},
];
const flattenStructure = data => {
return data.reduce((accumulator, item) => {
const items = item.data.reduce((vehicles, vehicle) => {
const modified = { ...vehicle, id: item.id };
return vehicles.concat(modified);
}, []);
return accumulator.concat(items);
}, []);
};
const search = (array, term) => {
const invalidTerm = term == null || typeof term != 'string' || term.replace(/ /g, '') == '';
const invalidArray = array == null || !Array.isArray(array);
if (invalidTerm || invalidArray) {
console.log("Invalid arguments provided.");
return array;
}
return flattenStructure(array).filter(vehicle => {
const match = vehicle.value.toLowerCase() == term.toLowerCase();
const contains = vehicle.value.toLowerCase().indexOf(term.toLowerCase()) > -1;
return match || contains;
});
};
console.log(search(list, 'car'));
console.log(search(list, 'truck'));
Generaly speaking, when dealing with filtering, avoid using same original array to display filtered results in template.
Concerning filtering function, this should do the trick:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
public list: any;
public orgList: any;
public filteredList: any;
ngOnInit() {
this.list = this.orgList = [
{
id: 'abc',
data: [
{ key: '1', value: 'car' },
{ key: '2', value: 'bus' },
{ key: '3', value: 'bike' },
{ key: '4', value: 'truck' },
{ key: '5', value: 'jeep' },
],
},
{
id: 'def',
data: [
{ key: '1', value: 'car' },
{ key: '2', value: 'bicycle' },
{ key: '3', value: 'train' },
{ key: '4', value: 'aeroplane' },
{ key: '5', value: 'jeep' },
],
},
];
}
filterData = (dataItem, term: string) => {
return dataItem.value.toLowerCase().indexOf(term.toLowerCase()) !== -1;
};
handleSearch = (event) => {
if (event.target.value.length === 0) {
this.filteredList = [];
return;
}
const term = event.target.value;
const temp = this.list.filter((fullItem) =>
fullItem.data.filter((dataItem) => this.filterData(dataItem, term))
);
this.filteredList = temp
.map((fullItem) => ({
...fullItem,
data: fullItem.data.filter((dataItem) =>
this.filterData(dataItem, term)
),
}))
.filter((fullItem) => fullItem.data.length > 0);
};
}

How to retrieve a particular property from array of objects based on multiple conditions using react and javascript?

i have an array of objects like below,
const arr_obj = [
{
id: '1',
jobs: [
{
completed: false,
id: '11',
run: {
id: '6',
type: 'type1',
},
},
{
completed: true,
id: '14',
run: {
id: '17',
type: 'type1',
},
},
{
completed: false,
id: '12',
run: {
id: '7',
type: 'type2',
},
},
],
},
{
id: '2',
jobs: [
{
completed: true,
id: '13',
run: {
id: '8',
type: 'type2',
},
},
{
completed: true,
id: '16',
run: {
id: '9',
type: 'type1',
},
},
{
completed: true,
id: '61',
run: {
id: '19',
type: 'type1',
},
},
],
},
{
id: '3',
jobs: [
{
completed: false,
id: '111',
run: {
id: '62',
type: 'type1',
},
},
],
},
],
and an arr_ids = ["1","2"]
now i have to filter those ids from arr_obj matchings arr_ids which i do like so
const filteredIds = arr_obj.filter(obj => arr_ids.includes(obj.id));
so the filtered_arrobj = [
{
id: '1',
jobs: [
{
completed: false,
id: '11',
run: {
id: '6',
type: 'type1',
},
},
{
completed: true,
id: '14',
run: {
id: '17',
type: 'type1',
},
},
{
completed: false,
id: '12',
run: {
id: '7',
type: 'type2',
},
},
],
},
{
id: '2',
jobs: [
{
completed: true,
id: '13',
run: {
id: '8',
type: 'type2',
},
},
{
completed: true,
id: '16',
run: {
id: '9',
type: 'type1',
},
},
{
completed: true,
id: '61',
run: {
id: '19',
type: 'type1',
},
},
],
},
]
Now i will have to get the ids from filtered_arrobj whose runs are of type "type1" and none of the jobs have completed: false.
so the expected output from filtered_arrobj is "2"
here in the above example id "1" is not taken because id "1" has job completed: true but it also has job completed: false for type "type1".
what i have tried?
const output = filtered_arrobj.map(obj => obj.jobs.map(job => job.run.type ===
"type1" && job.completed === true));
when i log the output its gives like so
[
[false,true,false],
[true,true,true]
]
this not giving me the id of the obj whose job has run of type = "type1" and which has no jobs completed: false.
how can i get that. could someone help me with this. I am new to programming and learning on the go. thanks.
Use Array.filter() on arrObj filtered by arrIds which you already did, after that filter for jobs array (inside above filter) which contains type as type1 and then check if filtered jobs has completed as true then return true otherwise false.
const arrObj = [
{
id: '1',
jobs: [
{
completed: false,
id: '11',
run: {
id: '6',
type: 'type1',
},
},
{
completed: true,
id: '14',
run: {
id: '17',
type: 'type1',
},
},
{
completed: false,
id: '12',
run: {
id: '7',
type: 'type2',
},
},
],
},
{
id: '2',
jobs: [
{
completed: true,
id: '13',
run: {
id: '8',
type: 'type2',
},
},
{
completed: true,
id: '16',
run: {
id: '9',
type: 'type1',
},
},
{
completed: true,
id: '61',
run: {
id: '19',
type: 'type1',
},
},
],
},
{
id: '3',
jobs: [
{
completed: false,
id: '111',
run: {
id: '62',
type: 'type1',
},
},
],
},
];
const arrIds = ["1", "2"];
const filteredIds = arrObj.filter(obj => {
if (arrIds.includes(obj.id)) {
const jobs = obj.jobs.filter(job => job.run.type === "type1");
if (jobs.length > 0) {
return jobs.every(job => job.completed === true);
}
return false;
}
return false;
});
console.log(filteredIds);
This example iterates over the data array with a simple for...of loop and uses every to check the required conditions.
const data=[{id:"1",jobs:[{completed:false,id:"11",run:{id:"6",type:"type1"}},{completed:true,id:"14",run:{id:"17",type:"type1"}},{completed:false,id:"12",run:{id:"7",type:"type2"}}]},{id:"2",jobs:[{completed:true,id:"13",run:{id:"8",type:"type2"}},{completed:true,id:"16",run:{id:"9",type:"type1"}},{completed:true,id:"61",run:{id:"19",type:"type1"}}]}];
let out = [];
for (let obj of data) {
// Find out if the jobs of type1 have all completed...
const runCompleted = obj.jobs.every(job => {
return job.completed && job.run.type === 'type1';
});
// If not add the id to the output array
if (!runCompleted) out.push(obj.id);
}
// And then you just check the length of the array
console.log(out);
console.log(out.length);
I would do it in one iteration, something like this:
const output = arr_obj.filter(obj => {
let hasType1 = false;
return (
arr_ids.includes(obj.id) &&
obj.jobs.every(job => {
if (job.run.type === 'type1') hasType1 = true;
return job.completed === true;
}) &&
hasType1
);
});
Or if you only need to check for completeness on type1 jobs:
const output = arr_obj.filter(obj => {
let hasType1 = false;
return (
arr_ids.includes(obj.id) &&
obj.jobs.every(job => {
if (job.run.type === 'type1') {
hasType1 = true;
return job.completed === true;
}
return true;
}) &&
hasType1
);
});
This should work :
const arr = arr_obj.reduce((accumulator, current) => {
const doSatisfyCondition = arr_ids.includes(current.id) && current.jobs.every(item => item.completed) && current.jobs.some(item => item.run.type === "type1")
if (doSatisfyCondition) accumulator.push(current.id);
return accumulator;
}, [])
console.log(arr)```
This code solves the problem, it is a bit focused on optimization but it works.
const arr=[{id:"1",jobs:[{completed:!1,id:"11",run:{id:"6",type:"type1"}},{completed:!0,id:"14",run:{id:"17",type:"type1"}},{completed:!1,id:"12",run:{id:"7",type:"type2"}}]},{id:"2",jobs:[{completed:!0,id:"13",run:{id:"8",type:"type2"}},{completed:!0,id:"16",run:{id:"9",type:"type1"}},{completed:!0,id:"61",run:{id:"19",type:"type1"}}]},{id:"3",jobs:[{completed:!1,id:"111",run:{id:"62",type:"type1"}}]}];
// For optimitation
const targets = ["1", "2"].reduce((a, c) => ({...a, [c]: true}), {})
const result = arr.filter(data => {
if(targets[data.id]) {
let containType1 = false
const jobs = data.jobs
const canSave = jobs.every(job => {
const isType1 = job.run.type === "type1"
containType1 = isType1 ? true : containType1
return isType1 ? job.completed : true
})
return canSave && containType1
}
return false
})
console.log(result)
Does this do the job?
const ids = new Set(arr_ids);
const filtered_objects = arr_obj.filter(el => ids.has(el.id));
The Set constructor creates a Set of Ids insread of an array. This enables you to use the has-function to check if the id property of the element coming from the arr_obj array is in the set "ids".
If you are new to programming you could check in to the Set and Map data structure and its related functions. There are some really good ways to filter and merge data if you can create some kind of uniqueness for every element in the array that you want to create a set from. In this example the Id is a good unique value.

How to delete particular nodes within a nested object tree in JavaScript

Here is where my algorithm skills ends. I can traverse through the object and find a certain object but I'm not able to delete the object in the same time.
Here is the object
const obj = {
children: [{
children: [
{
children: [
{
key: 'a1',
type: 'a1_type'
},
{
key: 'a2',
type: 'a2_type'
}
],
key: 'root',
type: 'root_type'
},
{
key: 'error',
type: 'error_type'
}
]
}]
}
The object with the key === 'error' object can be in any children array. I want to find it and delete the object that contains the key.
The output should be like that:
let output = findAndDeleteObjByKeyAndType('error', 'error_type')
output = {
children: [{
children: [
{
children: [
{
key: 'a1',
type: 'a1_type'
},
{
key: 'a2',
type: 'a2_type'
}
],
key: 'root',
type: 'root_type'
}
]
}]
}
Can someone help here?
Array methods like filter and every can come in handy here:
const object = {
children: [{
children: [{
children: [{
key: 'a1',
type: 'a1_type'
},
{
key: 'a2',
type: 'a2_type'
},
{
key: 'error',
type: 'error_type'
}
],
key: 'root',
type: 'root_type'
},
{
key: 'error',
type: 'error_type'
}
]
}]
}
function purgeAll (object, target) {
if (object.children) {
const keys = Object.keys(target)
object.children = object.children.filter(o =>
!keys.every(k => target[k] === o[k]) && purgeAll(o, target)
)
}
return object
}
let output = purgeAll(object, {
key: 'error',
type: 'error_type'
})
console.log(output)
.as-console-wrapper { min-height: 100%; }

Categories

Resources