Computed property getting called before prop initialization - javascript

In my Vue app, I am getting an array from map getters and I am trying to filter this array based on type which is a prop in a computed property. I checked both of them, both are strings, but the filter is not working properly, since I feel the computed property is called before the prop is initialized with value. Need some help with this?
all: 'mdm/all', // here 'mdm' indicates module name and 'all' is the state
prop: [type]
Inside computed, I have a property called
getData() {
const filteredData = this.all.filter(ele => ele.type === this.type.toLowerCase());
return filteredData.map(item => (
{name: item.name,
orderNo: item.order_no
});
}
In the above code, both ele.type and this.type seems to be strings with similar value (say 'expired') but the filteredData always happens to be an empty array.
Not sure what could be cause for this.

I would debug what .filter() is doing with a console log inside of it, for example:
this.all.filter(ele => {
console.log(`${ele.type} === ${this.type.toLowerCase()}?`, ele.type === this.type.toLowerCase());
return ele.type === this.type.toLowerCase();
})`
which will print out the result of ele.type === this.type.toLowerCase() every loop so you can verify what exactly is happening.

Related

Trying to see if object key exists keep getting undefined

I have a data response that responds with different objects in an array.
I have tried a variety of different methods to determine if the key 'name' exists including:
const hasName = array.forEach(item => "name" in item )
const hasName = array.forEach(item => Object.keys(item).includes('name') )
const hasName = array[0].hasOwnProperty("name") ? true : null
const hasName = array => array.some(x => x.some(({ name }) => name));
// lodash 'has' method
const hasName = has(array, 'name')
Array1 returns objects like:
[
{
name: 'cool name'
}
]
Array2 returns objects like:
[
{
group: 'cool group'
}
]
All of the methods I tried have either returned undefined or nothing at all and I'm completely at a loss for why. I have scoured Stack Overflow and the internet trying to get a better direction or an answer but I haven't been able to find anything.
You're not returning anything from some of your calls on the array. forEach for example runs a callback, and always returns undefined see docs. Your code just isn't working because you're using the functions incorrectly.
The code below filters your array to get the elements with a name property, then counts them to see whether one or more exists, which results in a true or false being stored in the hasName variable.
let myArr = [
{
name: 'cool name'
}
]
const hasName =
myArr
.filter(a => a.hasOwnProperty("name")) // filter for elements in the array which have a name property
.length // get the number of filtered elements
> 0 // check whether the number of elements in array with name prop is more than 0
console.log(hasName)
If you are sure that the "response" is fully received before the check, THEN
Your latest variant of the check can be implemented as follows:
array.some(obj => (obj.name !== undefined)); // fast, but not define .name if it is "undefined"
array.some(obj => obj.hasOwnProperty("name")); // slower, but will define .name with any value
Your solutions are generally correct, but it looks like you're a little confused,
Array.forEach always returns "undefined":
array.forEach(obj => {
console.log("name" in obj); // true for 1st array
console.log("group" in obj); // true for 2nd array
}); // return "undefined"
array[0].hasOwnProperty() works fine, and can't return "undefined" at all.
console.log(
array[0].hasOwnProperty("name") ? true : null; // return ("true" for the 1st array) AND ("null" for the 2nd)
);
When you used the Lodash, maybe you forgot to point to the object index?
_.has(array[0], 'name'); // return ("true" for the 1st array) AND ("false" for the 2nd)
Try a for loop
for (var i = 0; i < array.length; i++) {
if (array[i].hasOwnProperty('name')) {
console.log(array[i].name); //do something here.
break;
}
}

Cannot assign to read only property 'property' of object '#<Object>'

I have this problem.
With interface:
export interface IDevice {
id: string,
selected: boolean
}
creating instance by:
let newDevice: IDevice = {
id: uuid4(),
selected: false,
} as IDevice;
It gets added to array in recoil state, and in React used in a function where array has been retrieved with useRecoilState().
const [leftList, setLeftList] = React.useState<IDevice[]>([]);
Now it is being used in a handler for selecting the devices on a list control, here the error occurs:
...
leftList.map((item: IDevice) => {
if (item.id === event.dataItem.id) {
item.selected = !item.selected;
}
return item;
})
...
And I get the error: Cannot assign to read only property 'selected' of object '#'
Even cloning the array first by [...leftList] does not help.
I'm lost:-) Hope someone can spread light on this?
State shouldn't be modified directly, which is what you're currently doing now in your .map() method by updating the objects. TS is trying to tell you not to do this by making your objects read-only.
Instead, you can create a new object with all the properties from item (done using the spread syntax ...), along with a new overwritting property selected, which will use the negated version of the item's currently selected item if the id matches the event's data item id, or it'll keep the original selected value:
leftList.map((item: IDevice) => ({
...item,
selected: item.id === event.dataItem.id ? !item.selected : item.selected
}))
Cloning the array using the spread syntax ([...leftList]) will only do a shallow copy, and won't do a deep-copy of the objects within it, as a result, modifying the object references within .map() is still modifying the original state.
Or, instead of spreading the item object and creating a new object each time (which can hinder performance a little as pointed out by #3limin4t0r), you can instead only return a newly created object when you want to modify the selected property:
leftList.map((item: IDevice) => {
if (item.id !== event.dataItem.id) return item;
return {...item, selected: !item.selected};
});

Check if any property of object has null value

I have following array of objects data, that has multiple properties, some of them can have null value.
I want to filter through this array of objects and remove every object that has property of null value.
So far I have tried this code, but with no luck:
const noNull = data.filter((doc) => { return Object.values(doc).some(prop => { if (prop !== null) return doc; } )});
Any ideas how can I achieve this?
Thank you in advance.
Here Updated to keep items which do not have a null value
const noNull = obj.filter((doc) => { return Object.values(doc).every(prop => prop !== null)})
.some() and .every() expect boolean return value
reference

Destructuring fallback to prevent undefined error?

I have a list of array I do this:
const { id } = myArray.find(
(obj) => obj === true)
If the id is not present it will throw error. How to prevent error in the same time use destructuring? I want to keep the logic in one line.
The issue here is .find() returns undefined once there is no fulfillment for the condition:
The value of the first element in the array that satisfies the provided testing function. Otherwise, undefined is returned.
So probably you can use || operator to check if you have any value returned from .find() or you can replace with empty object {} instead on the right side of the operator.
Probably the option for one-liner is the following:
const myArray = [
{ id: 12 },
{ id: 13 },
{ id: 14 }
];
const { id } = myArray.find(e => e.id === 17) || {};
console.log(id);
So you can destructure id property even if it returned undefined in this way.
Also if you need you can add default value to your destructuring statement based on the documentation which states for Destructuring statement as follows:
A variable can be assigned a default, in the case that the value unpacked from the object is undefined.
const { id = 10 } = {};
console.log(id);
I hope this helps!

Using setState to dynamically populate an array of objects

I have an array of Objects in my props from my API but I need to modify the elements in this array of Objects and then pass it to a player on my application. I can successfully get my array of object but when I try to modify the elements and set the data into a state, The new state ends up having just the last item of my list. Here is my code,
if(musicitem.musics){
if(musicitem.musics.length === 0){
this.setState({musicLimited:true })
}
else{
return musicitem.musics.map((item, index)=>{
this.setState({
musics:[
...musics,
{
url:apiConstants.API_MUSIC_FILES+item.url,
cover:apiConstants.API_MUSIC_FILES+item.cover,
artist:item.artist,
}
]
})
});
}
}
I expect my state of musics to have an array of newly constructed objects but that is not the case. It ends up with just the last object that is expected to be in my array of objects. This means that, it has actually loop through the array of objects from props but the next State keeps overriding the previous state.
You should mention previous state's property(i.e. music) to get setState know that while using spread operator.
You can access previous state as passing first params to this.setState().
Example :
this.setState(prevState => ({ music : [...prevState.music, {artist:item.artist}] }))
Your code should be updated like this :
if(musicitem.musics){
if(musicitem.musics.length === 0){
this.setState({musicLimited:true })
}
else{
return musicitem.musics.map((item, index)=>{
this.setState(prevState => ({
musics:[
...prevState.musics, // use prevState
{
url:apiConstants.API_MUSIC_FILES+item.url,
cover:apiConstants.API_MUSIC_FILES+item.cover,
artist:item.artist,
}
]
}))
});
}
}

Categories

Resources