I have the following data structure:
this.state = {
active_menu: 2018,
info: [
{
key: 11,
title: 'A',
opened: false,
content: []
}, {
key: 10,
title: 'B',
opened: false,
content: []
}, {
key: 9,
title: 'C',
opened: false,
content: []
},
{
key: 1,
title: 'D',
opened: false,
content: []
}],
display: true
}
Tell me, please, how is it possible with the value of display:false to remove (perhaps it can assign style display:none) elements with keys 11, 10 and 9? At display:true elements 11, 10 and 9should be visible, and the element with key 1 is hidden.
Honestly I sit for the third day and can not decide. I ask for your help and would be grateful for any help...
Yes, i asking how to change the objects in the array
Since you're not allowed to modify state directly, you create a copy of info and its objects:
this.setState(({display, info}) => ({
info: info.map(entry => {
const {key} = entry;
const newEntry = {...entry};
if (!display && (key == 11 || key == 10 || key == 9)) {
newEntry.display = "none";
}
return newEntry;
})
}));
Or if it's okay to have a display property with the value undefined when it isn't "none":
this.setState(({display, info}) => ({
info: info.map(entry => ({
...entry,
display: !display && (entry.key == 11 || entry.key == 10 || entry.key == 9) ? "none" : undefined
}))
}));
I guess you can use the following code:
this.setState((prevState) => {
return {
...prevState,
display: false,
info: prevState.info.map(i => {
if(i.key ===9 || i.key === 10 || i.key === 11) return {...i, display: 'none' }
else return i
})
}
})
and something similar for the true case.
so what's going on in this code?
First, we use the setState's function argument instead of object one, because we need component's previous state in order to create the new one.
Next, we map over the prevState's info and try to create the new state based on that.
We do this by checking each item's key to see if it is equal to 9, 10 or 11 and if so, returning the modified object, elsewhere, we return the original item without changing it.
But I think your state structure is bad for this use case. so I suggest this one instead:
state = {
active_menu: 2018,
info: {
11: {
title: 'A',
opened: false,
content: []
},
....
}
}
And the following code for updating state:
this.setState((prevState) => {
return {
...prevState,
display: false,
info: {
...prevState.info,
11: {
...prevState.info[11],
display: 'none'
},
...
}
}
})
And the following one for mapping over the info items:
Object.keys(this.state.info).map(infoKey => {
let info = this.state.info[infoKey];
// Do whatever you want with info and return the component you want to render
})
Related
I want to make a function that filters the items by checking if they satisfy all of the conditions provided. The function receives 2 parameters: (data, include):
data: [
{ user: "mike#mail.com", rating: 20, disabled: false },
{ user: "greg#mail.com", rating: 14, disabled: false },
{ user: "john#mail.com", rating: 25, disabled: true }
]
include: [{ disabled: false }, { rating: 20 }]
With the data above, for example, the function should return the array with only one entry:
[{ user: "mike#mail.com", rating: 20, disabled: false }]
Because that entry has both disabled: false and rating: 20.
So my function is:
const filterInclude = (data, include) => {
const result = [];
data.forEach((item) => {
for (let [key, value] of Object.entries(item)) {
include.forEach((cond) => {
if (cond.hasOwnProperty(key) && cond[key] === value) {
result.push(item);
}
});
}
});
return result;
};
It works correctly when the include array has only 1 item, for example:
include: [{ disabled: false }]
However, when there are multiple conditions, it's not working correctly because it pushes the item to the result if it satisfies at least 1 condition.
How to improve this function to make it work correctly and push the item to the result only when it satisfies all of the conditions?
Just use the filter function as follows:
const filterInclude = ({data, disabled, rating })=> data.filter((el) => el.disabled === disabled && el.rating === rating )
Use it as follows:
const data= [
{ user: "mike#mail.com", rating: 20, disabled: false },
{ user: "greg#mail.com", rating: 14, disabled: false },
{ user: "john#mail.com", rating: 25, disabled: true }
];
console.log(filterInclude({data, disabled: false, rating: 20}))
So I accomplished what the task requires. Here is the improved code in case somebody needs something similar in the future:
const checkInclude = (item, include) => {
let numOfSatisfied = 0;
conditions: for (let cond of include) {
for (let [key, value] of Object.entries(item)) {
if (cond.hasOwnProperty(key) && cond[key] === value) {
numOfSatisfied++;
continue conditions;
}
}
}
return numOfSatisfied === include.length ? true : false;
};
const filterInclude = (data, include) => {
const result = data.filter((item) => checkInclude(item, include));
return result;
};
hope i am not late, just kidding.
let response = data.filter(each=>{
let count = include.reduce((a,item)=>{
const [keys,value] = Object.entries(item)[0]
if (each[keys] ===value) a++
return a
},0)
return count == include.length
})
console.log(response)
I am working in a task where I need to change the Boolean values onclick from an array of items, I need to change the Boolean value from array and child array. I have changed the value from an array it's working as expected but inside that array I am having an another array of objects from that I need to change the boolean value. OnClick I need to make parent isProcessing and child isProcessing false. I have tried changing it but I am not getting the expected output , can anyone guide me how to achieve this thanks in advance.
Mock Data:
const mockItems = [
{
id: '1',
itemType: 'metal',
doneBy: {
id: '1',
display: 'Item Name',
},
catg: 'A',
createdDate: '01/01/2021',
updatedBy: {
id: '1',
type: 'M-A',
},
isProcessing: 'true',
subItems: [
{
id: '1',
doneBy: {
id: '1',
display: 'sub item name',
},
status: {
type: 'notapproved',
},
isProcessing: 'true',
},
],
},
];
Code for accessing parent : isProcessing //it's working
const [processingItem, setProcessingItem] = useState(mockItems);
const handleToggle = () => {
setProcessingItem((prevState) =>
prevState.map((prItem, i) =>
i === 0 ? { ...prItem, isProcessing: false } : prItem
)
);
};
//code to change child array Boolean not working
const handleToggle = () => {
setProcessingItem((prevState) => {
prevState.map((prItem, index) => {
if (index === 0) {
const obj = { ...prItem.subItems, isProcessing: false };
return { ...prItem, isProcessing: false, obj };
}
});
});
};
Try this
const handleToggle = () => {
setProcessingItem((prevState) => {
prevState.map((prItem, index) => {
if(index !=0)
return prItem;
const subItems = prItem.subItems.map((si,idx)=>{
return idx != 0 ? si : {...si,isProcessing: false}
});
return { ...prItem, isProcessing: false, subItems:subItems }
}
)
}
)
}
I have object oriented data in the form:
var alist = [
'foo',
'foo.lol1',
'foo.lol2',
'bar.lol1',
'bar.barbar.kk',
...
]
which I would like to transform into a tree structure, to be able to serve them with a tree component (https://github.com/vinz3872/vuejs-tree in particular). The require form is the following:
var ok = [
{
text: "foo",
state: { expanded: false },
nodes: [
{
id: 1,
path: "foo.lol1",
text: "lol1",
checkable: true,
state: { checked: false },
},
{
id: 2,
path: "foo.lol2",
text: "lol2",
checkable: true,
state: { checked: false },
},
]
},
{
text: "bar",
state: { expanded: false },
nodes: [
{
id: 3,
path: "bar.lol1",
text: "lol1",
checkable: true,
state: { checked: false },
},
]
},
{
text: "bar",
state: { expanded: false },
nodes: [
{
id: 3,
path: "bar.lol1",
text: "lol1",
checkable: true,
state: { checked: false },
},
{
text: "barbar",
state: { expanded: false },
nodes: [
{
id: 4,
path: "bar.barbar.kk",
text: "kk",
checkable: true,
state: { checked: false },
},
]
},
]
}
]
I am aware that I should use recursion and I have tried all relevan posts in stackoverflow, i.e. How to build a JSON tree structure using object dot notation.
My main problem is that I have to somehow preserve the information of the full path to the leaves of the tree. As a newbie in js I lost myself in counters and callback for days without any luck.
I would appreciate your help.
Thank you in advance
Basically you could use forEach then split each string into array and then use reduce on that. Then you build nested object where the keys are current paths and also ad to result array.
var alist = [
'foo',
'foo.lol1',
'foo.lol2',
'bar.lol1',
'bar.barbar.kk',
]
const result = []
const levels = {
result
}
let prev = ''
let id = 1
alist.forEach(str => {
str.split('.').reduce((r, text, i, arr) => {
const path = prev += (prev.length ? '.' : '') + text
if (!r[path]) {
r[path] = {result: []}
const obj = {
id: id++,
text,
}
if (i === 0) {
obj.state = {expanded: false}
} else {
obj.state = {checked: false}
obj.checkable = true
obj.path = path
}
obj.nodes = r[path].result
r.result.push(obj)
}
if (i === arr.length - 1) {
prev = ''
}
return r[path]
}, levels)
})
console.log(result)
I found that it was easiest to do this transformation in two steps. The first converts your input into this format:
{
foo: {
lol1: {},
lol2: {}
},
bar: {
barbar: {
kk: {}
},
lol1: {}
},
}
The second uses just this format to create your desired structure. This has two advantages. First, I have tools lying around that make it easy to create this structure from your input. Second, this structure embeds enough information to create your output, with only one branching construct: whether the value at a path is an empty object or has properties. This makes the generation code relatively simple:
const setPath = ([p, ...ps]) => (v) => (o) =>
p == undefined ? v : Object .assign (
Array .isArray (o) || Number .isInteger (p) ? [] : {},
{...o, [p]: setPath (ps) (v) ((o || {}) [p])}
)
const reformat = (o, path = [], nextId = ((id) => () => String (++ id)) (0)) =>
Object .entries (o) .map (([k, v]) => Object .entries (v) .length > 0
? {text: k, state: {exapanded: false}, nodes: reformat (v, [...path, k], nextId)}
: {id: nextId (), path: [...path, k] .join('.'), text: k, checkable: false, state: {checked: false}}
)
const transform = (pathTokens) =>
reformat (pathTokens
.map (s => s .split ('.'))
.reduce ((a, path) => setPath (path) ({}) (a), {})
)
const alist = ['foo', 'foo.lol1', 'foo.lol2', 'bar.lol1', 'bar.barbar.kk']
console .log (transform (alist))
.as-console-wrapper {max-height: 100% !important; top: 0}
We start with setPath, which takes a path, in a format such as ['bar', 'barbar', 'kk'], the value to set at that path, and an object to shallow clone with this new property along that path. Thus setPath (['foo', 'bar', 'baz']) (42) ({foo: {qux: 7}, corge: 6}) yields {foo: {qux: 7, bar: {baz: 42}}, corge: 6}. (There's a little more in this reusable function to also handle array indices instead of string object paths, but we can't reach that from this input format.)
Then we have reformat, which does the format conversion. It simply builds a different input object based upon whether the input value is an empty object.
Finally, transform maps a splitting function over your input array to get the path structure needed for setPath, folds the results into an initially empty object by setting every path value to an empty object, yielding our intermediate format, which we then pas to reformat.
There is one thing I really don't like here, and that is the nextId function, which is a stateful function. We could just have easily used a generator function, but whatever we do here, we're using state to build this output and that bothers me. If someone has a cleaner suggestion for this, I'd love to hear it.
I want to check each object in an array to see if certain things exist. So let's say my array looks like this:
const arrayOfItems = [
{
delivery_method: {
delivery_method: 'car',
delivery_rate: 1,
pickup_day: 'none',
},
total_cost: 5,
items: [{}],
},
{
delivery_method: {
delivery_method: 'pickup',
pickup_day: 'T',
delivery_rate: 0,
},
total_cost: 5,
items: [{}],
},
]
And now I have a check methods function that looks like this:
async checkMethodChosen() {
let check = await arrayOfItems.map((item) => {
if (
(item.delivery_method.delivery_method === 'pickup'
&& item.delivery_method.pickup_day !== 'none')
|| item.delivery_method.delivery_method === 'car'
|| item.delivery_method.delivery_method === 'bike'
) {
return false
}
return true
})
let deliveryChosen = check.includes(false)
this.setState({
deliveryChosen,
})
}
The function sets the state with true or false if delivery_method is set to 'pickup' and the pickup_day is selected OR if delivery_method === 'car' or 'bike' . This works fine if there's only one object in the array. It's not working if there are multiple objects.
What I want to happen is if there are multiple objects, then this.state.deliveryChosen should only be true if delivery_method has been selected in each object. If it hasn't been selected for one object, then this.state.deliveryChosen should be false.
Thanks!
The function you are looking for is every() it will return true if the callback returns true for every item in an array.
For example here's a simplified version that just returns the boolean:
const arrayOfItems = [{delivery_method: {delivery_method: 'car',delivery_rate: 1,pickup_day: 'none',},total_cost: 5,items: [{}],},{delivery_method: {delivery_method: 'pickup',pickup_day: 'T',delivery_rate: 0,},total_cost: 5,items: [{}],},]
function checkMethodChosen(arr) {
// will return true if every item of arr meets the following condition:
return arr.every((item) =>
(item.delivery_method.delivery_method === 'pickup' && item.delivery_method.pickup_day !== 'none')
|| item.delivery_method.delivery_method === 'car'
|| item.delivery_method.delivery_method === 'bike'
)
}
console.log(checkMethodChosen(arrayOfItems))
I have a basic tree data structure like this (forget about the parent properties that are missing):
data = {
name: 'root',
value: 20,
children: [
{
name: 'child_1',
value: 12,
children: [...]
},
{
name: 'child_2',
value: 8,
children: [...]
},
]
}
And I want to write a function that takes in any item, which can be the root, or any of the children, and do some evaluation on it.
Like the following:
public doCheck(item: TreeItem): boolean {
item.children.forEach( (i: TreeItem) => {
return this.doCheck(i);
});
return (item.children.some( (i: TreeItem) => i.value >= 10));
}
However, right now this seems to be traversing the tree properly, but only returns the evaluation (item.children.some( (i: TreeItem) => i.value >= 10)) as if it was called on the root item alone, for which it will never be true.
Where am I going wrong?
You want to get rid of the forEach and instead recurse inside the some.
I'm going to assume value appears on the entries in children. If so:
function doCheck(item) {
// If `children` is optional, you could add
// `item.children &&` just after `return`
return item.children.some(entry => entry.value >= 10 || doCheck(entry));
}
console.log(doCheck(data)); // true or false
var data = {
name: 'root',
data: [],
value: 5,
children: [
{
name: 'child_1',
data: [],
children: [],
value: 10,
},
{
name: 'child_2',
data: [],
children: [],
value: 20
},
]
};
function doCheck(item) {
// If `children` is optional, you could add
// `item.children &&` just after `return`
return item.children.some(entry => entry.value >= 10 || doCheck(entry));
}
console.log(doCheck(data)); // true, since `child_1` has it
You'll need to add back the type annotations, etc., to turn that back into TypeScript.
If you wanted to find the entry, not just check for it, you'd use find instead of some:
function doCheck(item) {
// If `children` is optional, you could add
// `item.children &&` just after `return`
return item.children.find(entry => entry.value >= 10 || doCheck(entry));
}
console.log(doCheck(data)); // undefined, or the child
var data = {
name: 'root',
data: [],
value: 5,
children: [
{
name: 'child_1',
data: [],
children: [],
value: 10,
},
{
name: 'child_2',
data: [],
children: [],
value: 20
},
]
};
function doCheck(item) {
// If `children` is optional, you could add
// `item.children &&` just after `return`
return item.children.find(entry => entry.value >= 10 || doCheck(entry));
}
console.log(doCheck(data).name);// "child_1"
Not clear what exactly you're trying to do, but this part:
item.children.forEach( (i: TreeItem) => {
return this.doCheck(i);
});
Makes little sense, as you're basically not doing anything here.
You're probably looking for this:
public doCheck(item: TreeItem): boolean {
return
item.children.some(i => i.value >= 10)
&&
item.children.some(i => this.doCheck(i));
}
Maybe || instead of &&.
Which can of course be changed to:
public doCheck(item: TreeItem): boolean {
return item.children.some(i => i.value >= 10 && this.doCheck(i));
}