I have a tree object which is an irregular tree which children's names and key values can change everytime I run my code. For example:
{
addressRouter: 192.168.0.1,
addresses:
{
address1: 'A',
},
{
address2: 'B',
},
{
ports: [
{
portA: 'C',
portB: null
},
}
route: 'D',
}
so the names: 'addressRouter', 'addresses', 'address1', etc and their keys are unpredictable but I need to convert the tree object in arrays with the following format:
addressRouter
addresses/address1
addresses/address2
addresses/ports/portA
addresses/ports/portB
route
and then have their keys next to them.
I have this function to construct the tree, which is correct:
const iterate = (obj, obj2) => {
Object.keys(obj).forEach(key => {
obj2[key] = obj[key];
if (typeof obj[key] === 'object') {
iterate(obj[key], obj2)
}
})
}
but after debugging, I realized it doesn't get all branches.
We can use a recursive function to traverse the tree and get the keys in the required format.
I am assuming addresses in the given tree object is an array of objects
function processTree(obj, rootKey) {
const arr = [];
obj && Object.keys(obj).forEach(key => {
const val = obj[key];
if (val && val instanceof Array) {
val.forEach(item => arr.push(...processTree(item, key)))
}else if (val && typeof(val) == "object") {
arr.push(...processTree(val, key));
}else {
arr.push(key);
}
});
return rootKey ? arr.map(item => rootKey + "/" + item) : arr;
}
console.log(processTree(tree, null));
Result : ["addressRouter", "addresses/address1", "addresses/address2", "addresses/ports/portA", "addresses/ports/portB", "route"]
I just write the code below, see if it's what you want.
const tree = {
addressRouter: '192.168.0.1',
addresses: [
{
address1: 'A',
},
{
address2: 'B',
},
{
ports: [
{
portA: 'C',
portB: null,
},
],
},
],
route: 'D',
};
const traverse = (input) => {
const resultList = [];
const isEndPoint = (obj) => {
return typeof obj !== 'object' || obj === null;
};
const buildPath = (currentPath, key) =>
currentPath === '' ? key : `${currentPath}/${key}`;
const innerTraverse = (tree, currentPath = '') => {
if (tree !== null && typeof tree === 'object') {
Object.entries(tree).forEach(([key, value]) => {
if (isEndPoint(value)) {
resultList.push(buildPath(currentPath, key));
return;
}
let path = currentPath;
if (!Array.isArray(tree)) {
path = buildPath(currentPath, key);
}
innerTraverse(value, path);
});
}
};
innerTraverse(input);
return resultList;
};
console.log(traverse(tree));
/**
* [
* 'addressRouter',
* 'addresses/address1',
* 'addresses/address2',
* 'addresses/ports/portA',
* 'addresses/ports/portB',
* 'route'
* ]
*/
I've just found the loop I needed it here, which is:
function* traverse(o,path=[]) {
for (var i of Object.keys(o)) {
const itemPath = path.concat(i);
yield [i,o[i],itemPath];
if (o[i] !== null && typeof(o[i])=="object") {
//going one step down in the object tree!!
yield* traverse(o[i],itemPath);
}
}
}
Then, if the tree object (first gray box in my question) were name, for instance, "params", I would do this:
if (params != null && params != undefined) {
for(var [key, value, path] of traverse(params)) {
// do something here with each key and value
if (typeof value == 'string'){
var tempName = '';
for (name in path) {
//console.log(path[name])
p= tempName += path[name] + "/"
}
console.log(p, value)
}
}
}
Related
I'm using this react-beautiful-dnd library to be able to reorder lists. However, even though I'm able to drag and drop and re-order, there is a flicker when I try to move a card from one list to another list I call API when a card is dragged to the destination list
const onDragEnd = (result: any) => {
if (!result.destination) {
return;
}
const listCopy: any = { ...elements };
const sourceList = listCopy[result.source.droppableId];
const [removedElement, newSourceList] = removeFromList(
sourceList,
result.source.index
);
listCopy[result.source.droppableId] = newSourceList;
const destinationList = listCopy[result.destination.droppableId];
listCopy[result.destination.droppableId] = addToList(
result.destination.droppableId,
destinationList || [],
result.destination.index,
removedElement,
result.source.droppableId
);
setElements(listCopy)};
and in addToList function I am calling API to update order on server
const addToList = (
changedList: string,
list: any[],
index: number,
element: any,
currentListId: string
) => {
let cardOrder;
const result = Array.from(list);
result.splice(index, 0, element);
const cardCurrentIndex = result.findIndex((item) => item.id === element.id);
if (list.length === 0) {
cardOrder = DEFAULT_PIPELINE_ORDER;
} else if (cardCurrentIndex === 0 && result.length !== 0) {
const nextCardOrder = result[1];
cardOrder = nextCardOrder.current_stage_order - STAGE_INCREMENT_AMOUNT;
} else if (cardCurrentIndex === result.length - 1) {
const nextCardOrder = result[result.length - 2];
cardOrder = nextCardOrder.current_stage_order + STAGE_INCREMENT_AMOUNT;
} else if (
Boolean(result[cardCurrentIndex - 1]) &&
Boolean(result[cardCurrentIndex + 1])
) {
cardOrder = Math.round(
(result[cardCurrentIndex - 1].current_stage_order +
result[cardCurrentIndex + 1].current_stage_order) /
2
);
}
let candidatesData: any = elements;
if (candidatesData) {
if (currentListId === changedList) {
candidatesData[changedList as any] = result as unknown as elementsType;
setElements([...candidatesData]);
} else {
candidatesData[currentListId as any] = candidatesData[
currentListId as any
]?.filter((item: any) => item.id !== element.id);
candidatesData[changedList as any] = result as unknown as elementsType;
setElements([...candidatesData]);
console.log("[...candidatesData]", [...candidatesData]);
}
}
const stageId = stagePipeLineLanes?.find(
(item) => item.id.toString() === changedList.toLowerCase()
)?.id;
if (
changedList === "applied" ||
changedList === "sourcing" ||
changedList === "interviewing"
) {
const changedDestination = changedList;
const destinationStages = positionDetails?.candidate_stages.filter(
(item) =>
item.pipeline.toLowerCase() === changedDestination.toLowerCase()
);
const stage = destinationStages.find((item) => item.is_default === true);
mutate(
{
candidateId: element.id.toString(),
data: compressObject({
stage: stage?.id.toString(),
}),
},
{
onSuccess: (response) => {
if (response) {
toast.success(
`Candidate moved to ${capitalizeFirstLetter(
changedDestination
)}`
);
}
},
}
);
} else {
mutate({
candidateId: element.id.toString(),
data: compressObject({
stage: stageId?.toString() || "",
current_stage_order: cardOrder?.toString() || "",
}),
});
}
return result;
};
I want to make a generic filter function. Currently I have a function that looks like this:
const filterRows = () => {
items.filter((item) => {
if(selectedDrinks.length > 0 && selectIds.length > 0) {
return selectedDrinks.includes(item.description) && selectedIds.includes(item.props.id)
}else if(selectedDrinks.length > 0) {
return selectedDrinks.includes(item.description)
}else if(selectedIds.length > 0) {
return selectedIds.includes(item.props.id)
}
}
}
The number of if checks I need to do will grow exponentially if I add one more thing to filter by.
I've made a pathetic try below. One issue I encountered is if I have a nested structure and want to access ["props/id"] as I don't know the syntax for it. Also tried ["props:id"] etc. And if I add multiple strings in the query it does not work either. And even if I could add multiple strings properly it would only work as an OR.
And for me it would be selectedDrinks && selectedId as both need to match for it to filter, not selectedDrinks || selectedIds
I want to include everything in both selectedDrinks and selectedIds as a query, and they should filter only if both are included in "assets" as description and props:id. I should also be able to add e.g "selectedNames" as a third "query parameter".
const selectedDrinks: string[] = [
"cola",
"fanta",
]
const selectedIds : string[] = [
"5",
"4",
]
interface s {
description: string;
name: string;
props: {
id: string
}
}
const items: s[] = [
{
description: "cola",
name: "computer",
props: {
id: "4"
}
},
{
description: "fanta",
name: "laptop",
props: {
id: "5"
}
},
{
description: "sprite",
name: "phone",
props: {
id: "6"
}
}
]
export function genericFilter<T>(
object: T,
filters: Array<keyof T>,
query: string[]
):boolean {
if(query.length === 0)
return true
return filters.some(filter => {
const value = object[filter]
console.log(value)
if(typeof value === "string") {
return value.toLowerCase().includes(query.map(q => q.toLowerCase()).join(""))
}
if(typeof value === "number") {
return value.toString().includes(query.map(q => q.toLowerCase()).join(""))
}
return false
})
}
const myFilterResult = items.filter((asset) => genericFilter(item, ["props", "name"], ["5"]))
console.log(myFilterResult)
If anyone is interested, here is how I solved it.
/**
*
* #returns A new list of filtered objects
* #param objects The objects that we want to filter
* #param properties The properties we want to apply on the object and compare with the query
* #param queries The queries we want to filter by
*/
export function genericFilter<T>(
objects: T[],
properties: Array<keyof T>,
queries: Array<string>[] | Array<number>[]
):T[] {
return objects.filter((object) => {
var count = 0;
properties.some((props) => {
const objectValue = object[props]
if(typeof objectValue === "string" || typeof objectValue === "number") {
queries.forEach((query) => {
query.forEach((queryValue) => {
if(queryValue === objectValue) {
count+=1;
}
})
})
}
})
return count === properties.length;
})
}
export default genericFilter;
How you call the function, can include X amount of filters and strings to search for.
const result = genericFilter(assets, ["description", "id", "name"], [selectedAssetTypes, selectedIds, selectedNames])
I am very new to react and have a very straightforward usecase.
on a certain function call, I need to update one of the state variables - which is an array of objects.
I need to iterate through this array find an element and add a key the object in that element.
I tried this way but its not working.
const [finalStudents, setFinalStudents] = useState([]);
function setAttentionForStudent(deviceName, value) {
// console.log("Device ID:", deviceName)
// console.log("Attention value:", value)
finalStudents.map((student, index) => {
console.log("student", student)
if (student['device']['deviceName'] == deviceName) {
console.log("student inside", student)
setFinalStudents((prevFinalStudents) => {
console.log("prev final student",prevFinalStudents)
prevFinalStudents[index]['device']['attentionValue'] = value
})
// student['device']['attentionValue'] = value
} else {
setFinalStudents((prevFinalStudents) => {
prevFinalStudents[index]['device']['attentionValue'] = 0
})
}
})
// console.log(finalStudents)
}
Try this:
const [finalStudents, setFinalStudents] = [];
const setAttentionForStudent = (deviceName, value) => {
if (finalStudents.length !== 0) {
for (var x = 0; x < finalStudents.length; x++) {
if (finalStudents[x].device.deviceName === deviceName) {
finalStudents[x].device.deviceName = value;
setFinalStudents(new Array(...finalStudents));
} else {
finalStudents[x].device.deviceName = value;
setFinalStudents(new Array(...finalStudents));
}
}
}
};
callback in setFinalStudents should return an array to update state. You can use map in setFinalStudents like this:
setFinalStudents((prevFinalStudents) => {
return prevFinalStudents.map((student) => {
if (student["device"]["deviceName"] == deviceName) {
student["device"]["attentionValue"] = value;
} else {
student["device"]["attentionValue"] = value;
}
return student;
});
});
Was finally able to solve the problem by the following way:
setFinalStudents((prevFinalStudents) => {
const clonedFinalStudents = [...prevFinalStudents];
return clonedFinalStudents.map((student) => {
let updatedStudent = { ...student };
let attentionValue = 0;
if (student['device']['deviceName'] == deviceName) {
attentionValue = value;
}
updatedStudent = {
...updatedStudent,
device: {
...updatedStudent.device,
attentionValue,
},
};
return updatedStudent;
});
});
i have a object like
obj = {name:"xxx" , des1:"x",des2:"xx",des3:"xxx" , age:"12"}.
But the property of des can be incresed as des1,des2,des3,des4 ... according to the users inputs. So basically we don't know how much of "des" properties are there in the object.
I want do something like this. Grab all the properties of des and put them in array. Then update the object as follows
obj = {name:"xxx" , description:["x","xx","xxx"] , age:"12"}
how can I achieve this using ES6 syntax
you can transform your data in this way:
const transformed = Object.keys(obj).reduce(
(acc, key) => {
return key === 'name' || key === 'age'
? { ...acc, [key]: obj[key] }
: { ...acc, description: [...acc.description, obj[key]] }
},
{ description: [] }
)
What about this one?
const f = {name:"xxx", des1:"x", des2:"xx", des3:"xxx", age:"12"};
const { name, age, ...rest} = f;
const result = { name, age, description: Object.values(rest) };
console.log(result) // { name: 'xxx', age: '12', description: [ 'x', 'xx', 'xxx' ] }
You can make use of reduce and then match the string with the regex which checks if the string is des, followed by a number
var obj = {name:"xxx" , des1:"x",des2:"xx",des3:"xxx" , age:"12"}
const res = Object.keys(obj).reduce((acc, key)=> {
if(key.match(/^des([0-9]+)$/)) {
if(acc.description) {
acc.description.push(obj[key]);
} else {
acc.description = [obj[key]];
}
} else {
acc[key] = obj[key];
}
return acc;
}, {})
console.log(res);
I have a function called renderExercises which I call in my render function. renderExercises returns an array of ExercisesChoose components.
renderExercises() {
const {selectedType} = this.state;
const allExercises = this.props.exercises;
let exercisesToRender = [];
if (selectedType !== 'all') {
exercisesToRender = allExercises[selectedType];
} else {
exercisesToRender = Object.values(allExercises)
.reduce((array, subarray) => array.concat(subarray), [])
.sort();
}
return exercisesToRender.map((exercise) => {
return (
<ExercisesChoose
key={exercise}
name={exercise}
/>
)
})
}
So far this works. However I also want to filter based on search text if the user has entered this text.
This isn't working as filter can't be called on the existing array exercisesToRender.
if (typeof this.searchText !== 'undefined') {
const searchText = this.searchText.value;
// This is not working
exercisesToRender.filter(item => {
return item.includes(searchText);
});
}
What is the solution to this? Is there a sort method that allows for mutation? If so, is this advisable to use?
This is my current solution which works but is pretty ugly:
renderExercises() {
const {selectedType} = this.state;
const allExercises = this.props.exercises;
let exercisesToRender = [];
if (selectedType !== 'all') {
exercisesToRender = allExercises[selectedType];
} else {
// Combine all the different exercise groups into a single array
exercisesToRender = Object.values(allExercises)
.reduce((array, subarray) => array.concat(subarray), [])
.sort();
}
let render = [];
if (typeof this.searchText !== 'undefined') {
const searchText = this.searchText.value;
render = exercisesToRender.filter(item => {
return item.includes(searchText);
});
} else {
render = exercisesToRender;
}
return render.map((exercise) => {
return (
<ExercisesChoose
key={exercise}
name={exercise}
/>
)
})
}
This is what my exercises object looks like:
this.props.exercises = [
legs:["Squat", "Power squats", "Burpees"]
pull:["Pull up", "Chin up", "Dumbbell curl", "Horizontal row"]
push:["Push up", "Bench press", "Dumbbell bench press", "Mountain climbers"]
cardio: ["Running high knees", "Plank", "Crunches", "Skipping"]
]
My strategy for this case would be:
reduce to filter exercises by type
filter them by searchText
sort
map to render
Final result:
renderExercises() {
const { selectedType } = this.state
const { exercises: allExercises } = this.props
return Object
.keys(allExercises)
.reduce((result, key) => {
if (selectedType === 'all' || key === selectedType) {
return [
...result,
...allExercises[key],
]
}
return result
}, [])
.filter(exercise => searchText ? exercise.includes(searchText) : true)
.sort()
.map(exercise =>
<ExercisesChoose
key={exercise}
name={exercise}
/>
)
}
const exercises = {
legs:["Squat", "Power squats", "Burpees"],
pull:["Pull up", "Chin up", "Dumbbell curl", "Horizontal row"],
push:["Push up", "Bench press", "Dumbbell bench press", "Mountain climbers"],
cardio: ["Running high knees", "Plank", "Crunches", "Skipping"],
}
const filterExercises = (type, searchText) => {
return Object
.keys(exercises)
.reduce((result, key) => {
if (type === 'all' || key === type) {
return [
...result,
...exercises[key],
]
}
return result
}, [])
.filter(exercise => searchText ? exercise.includes(searchText) : true)
.sort()
.join(', ')
}
console.log('All exercises:', filterExercises('all', ''))
console.log('All (up):', filterExercises('all', 'up'))
console.log('Push:', filterExercises('push', ''))
console.log('Push (press):', filterExercises('push', 'press'))
Ive expanded slightly on mersocarlin's answer as I was getting some false results from searchText, but essentially his logic does work.
renderExercises() {
const {selectedType} = this.state;
const allExercises = this.props.exercises;
let searchText = false;
if (this.searchText && this.searchText.value.length > 0) {
searchText = this.searchText.value.toLowerCase();
}
return Object
.keys(allExercises)
.reduce((result, key) => {
if (selectedType === 'all' || key === selectedType) {
return [
...result,
...allExercises[key]
]
}
return result
}, [])
.filter(exercise => searchText ? exercise.toLowerCase().includes(searchText) : true)
.map((exercise) => {
let active = false;
if (this.props.chosenExercise === exercise) {
active = true;
}
return (
<ExercisesChoose
key={exercise}
name={exercise}
active={active}
setNumber={this.props.number}
updateValue={this.props.updateValue}
/>
)
})
}