Here is my initial code that works flawlessly.
const objNotes = [
{},
{
title: "Be better",
body: "Do better"
},
{
title: "You are what you eat",
body: "Eat well and responsibly"
},
{
title: "Look good",
body: "Work good"
}
];
const findNote = (notes, noteTitle) => {
const index = notes.findIndex((note, index) => {
return note.title === noteTitle;
});
return notes[index];
};
const action = findNote(objNotes, "Look good");
console.log(action);
When I attach the method .toLowerCase like down below I get:
TypeError: Cannot read property 'toLowerCase' of undefined
and I don't understand why.
const findNote = (notes, noteTitle) => {
const index = notes.findIndex((note, index) => {
return note.title.toLowerCase() === noteTitle.toLowerCase();
});
return notes[index];
};
Your first object does not have the property title, trying to toLowerCase() that is throwing the error.
You can check if the property in object exists or not before using toLowerCase():
const objNotes = [
{},
{
title: "Be better",
body: "Do better"
},
{
title: "You are what you eat",
body: "Eat well and responsibly"
},
{
title: "Look good",
body: "Work good"
}
];
const findNote = (notes, noteTitle) => {
const index = notes.findIndex((note, index) => {
return note.title == undefined? '' : note.title.toLowerCase() === noteTitle.toLowerCase();
});
return notes[index];
};
const action = findNote(objNotes, "Look good");
console.log(action);
Use Array.find() when you want the item and not the index of the item.
To prevent the error when you call a string method on an undefined value, you can use short-circuit evaluation note.title !== undefined && .... Assuming the note.title is always a string if not undefined, an undefined value would return false immediately, and if it's not undefined the rest of the expression (the comparison) would be evaluated:
const objNotes = [{},{"title":"Be better","body":"Do better"},{"title":"You are what you eat","body":"Eat well and responsibly"},{"title":"Look good","body":"Work good"}];
const findNote = (notes, noteTitle) =>
notes.find((note, index) => // use Array.find()
note.title !== undefined && // the title is not undefined
note.title.toLowerCase() === noteTitle.toLowerCase() // compare the strings
);
const action = findNote(objNotes, "Look good");
console.log(action);
Related
I need help filtering an array of objects in a typescript react native project using state to store array values and filter objects in the array
see error I keep getting in the method in question
cannot read property push of undefined
Appearing this way
LOG after item
LOG inside 300
ERROR TypeError: Cannot read property 'push' of undefined, js engine: hermes
see my code below
const handleSearchButtonPressed = () => {
console.log("inside handleSearchButtonPressed")
if (!itemListStore){
return
}
const text = searchText.toLowerCase()
console.log("inside 100")
// array of output objects
// eslint-disable-next-line array-callback-return
const filteredObjs = itemListStore.filter((item) => {
console.log(" 200")
console.log(" item is ")
console.log(item)
console.log(" after item")
const strTitle = JSON.stringify(item.title)
const strDesc = JSON.stringify(item.description)
const strLink = JSON.stringify(item.link)
const itemObj = {
title:strTitle,
description: strDesc,
link: strLink
}
if (strTitle.toLowerCase().match(text)) {
console.log("inside 300")
filteredObjs.push(itemObj)
console.log("inside 400")
}
})
console.log("filteredObjs", filteredObjs)
if (!text || text === "") {
setSearchText("",
)
} else if (!Array.isArray(filteredObjs) && !filteredObjs.length) {
// set no data flag to true so as to render flatlist conditionally
setNoData(true)
} else if (Array.isArray(filteredObjs)) {
setNoData(false)
setItemListStore(filteredObjs)
}
}
You're trying to both filter and map. This can more easily be achieved using either two separate operations or one single reduce.
Two operations...
const filteredObjs = itemListStore
.filter(({ title }) => title.toLowerCase().includes(text))
.map(({ title, description, link }) => ({
title: JSON.stringify(title),
description: JSON.stringify(description),
link: JSON.stringify(link),
}));
One reduce...
const filteredObjs = itemListStore.reduce(
(arr, { title, description, link }) => {
if (title.toLowerCase().includes(text)) {
return [
...arr,
{
title: JSON.stringify(title),
description: JSON.stringify(description),
link: JSON.stringify(link),
},
];
}
return arr;
},
[]
);
It works like this: I have a table made in Vue, where I have some select options. This error appears when I have a grupo (group) and this group is not associated with a maquina (machine), what shouldn't happen, the objective is that only "-" appears. Throws an error in the console and does not show in my DataTable.
The error: [/Vue warn]: Error in render: "TypeError: Cannot read properties of undefined (reading 'id_area')
This is the part of my code that I believe is causing this error:
computed: {
linhas () {
return this.lista.map(row => {
const group = this.grupos.find(g => g.id === row.id_grupo);
const machine = this.maquinas.find(m => m.id === group.id_maquina);
const area = this.areas.find(a => a.id === machine.id_area);
return ({
href: {path: this.editRoute + row.id},
cols: [
row.id,
row.tag,
row.descricao,
row.tipo === "ANALOGICA" ? "Analógica" : "Digital",
group.nome,
(machine || { nome: "-" }).nome,
(area || { nome: "-" }).nome
]
});
});
}
},
Can someone help me? I do not understand why this happening.
The array.find() method returns undefined if the value is not found (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find).
So if ther is no "machina" with the id group.id_maquina retrieve from the previous line, the line const machine = this.maquinas.find(m => m.id === group.id_maquina); set value undefined to the machine const.
And here's your error happens: reading id_area from machine that is undefined.
So you have to check if your variables are not undefined after array.find()
//...
const group = this.grupos.find(g => g.id === row.id_grupo);
if (group) {
const machine = this.maquinas.find(m => m.id === group.id_maquina);
if (machine) {
const area = this.areas.find(a => a.id === machine.id_area);
}
}
//...
I have written this code to filter a "books" array, depending on the author or genre have been given as a parameter:-
//This code works perfectly fine as is
allBooks: (root, args) => {
if (args.author === undefined && args.genre === undefined) {
return books
} else if (args.author === undefined) {
return books.filter((book) => book.genres.includes(args.genre))
} else if (args.genre === undefined) {
return books.filter((book) => book.author === args.author)
} else {
const booksFilter = books.filter((book) => book.author === args.author)
return booksFilter.filter((book) => book.genres.includes(args.genre))
}
}
I believe there must be some way to write this more "professionally" without using all these if-else. So if anyone knows a better way, I'll appreciate it.
[Edited]
Thanks to all, I decided to go with ghostkraviz solution, code looks like this now:
allBooks: (root, args) => {
return books.filter((book) => {
const filteredGenres = book.genres.filter((genre) =>
genre.includes(args.genre || "")
);
return book.author.includes(args.author || "") && filteredGenres.length > 0;
})
}
You could take an array for filtering with key/value pairs, like
filters = [
['author', 'eliot'],
['genre', 'fiction']
]
and an object for storing special type of searching, like
methods = {
genre: 'includes'
}
Together, you get the following function
result = books.filter(book => filter.every(([key, value]) => key in methods
? book[key][methods[key]](value)
: book[key] === value
));
Because of Array#every's return value of true for empty arrays, you need no further action to get all books.
since String.prototype.includes actually checks if a string maybe found within another string (the parameter) MDN String.prototype.includes. that means, for undefined args you could default it to empty string. Empty strings will return true if checked with .includes from any string.
you only check for 2 args which are the author & genre.
here's the example:
const books = [
{author: "A", genres: ["Horror", "romance"]},
{author: "B", genres: ["romance"]},
{author: "X", genres: ["science"]},
{author: "C", genres: ["science", "Horror"]}
];
const allBooks = (root, args) => {
return books.filter(book => {
const filteredGenres = book.genres.filter(genre =>
genre.includes(args.genre || "")
);
return book.author.includes(args.author || "") && filteredGenres.length > 0;
});
};
console.log('filtered Horror = ', allBooks({}, {genre: 'Horror'}));
console.log('filtered A and Horror = ', allBooks({}, {author: 'A', genre: 'Horror'}));
console.log('filtered romance = ', allBooks({}, {genre: 'romance'}));
// for all books result
console.log('filtered romance = ', allBooks({}, {}));
// for an author result
console.log('filtered author(A) = ', allBooks({}, {author:"A"}));
I don't know if the version below is written more "professionally" as it looks messy, but it is a single line, and does not use if-else.
books = [{
author: "a",
genres: ["a"]},
{author: "a",
genres: ["a", "b"]},
{author: "b",
genres: ["c", "b"]
}];
allBooks = (root, args) => {
return (!args.author && !args.genre) ? books : (!args.author) ?
books.filter((book) => book.genres.includes(args.genre)):(!args.genre)?
books.filter((book) => book.author === args.author) :
(books.filter((book) => book.author === args.author)).filter((book) =>
book.genres.includes(args.genre));
}
console.log(allBooks("",{genre: "b"}));
//console:
//0: {author: "a", genres: Array(2)}
//1: {author: "b", genres: Array(2)}
I like to use if-else, as it's more readable. We can also eliminate the else-ifs since each of the if statement has a return.
This is just a simplified version of your existing codes without the else-ifs and curly brackets
allBooks: (root, args) => {
const { author, genre } = args;
if (!author && !genre) return books;
if (!author) return books.filter((book) => book.genres.includes(genre))
if (!genre) return books.filter((book) => book.author === author);
return books.filter((book) => book.author === author).filter((book) => book.genres.includes(genre))
}
Sometimes it's not about writing codes professionally.
It's about writing codes that you can easily understand.
Let's assume that we have the following .filter function:
search(searchQuery: string) {
let results = myArray.filter(item =>
item.title.toLowerCase().includes(searchQuery) ||
item.description.toLowerCase().includes(searchQuery)
);
return results;
}
For this example we can consider that item doesn't have a description property always but only some times.
In this case function will fail with an error:
ERROR TypeError: Cannot read property 'toLowerCase' of undefined
How can we can still search in the array and include the description property in the search only if it exists? Is this possible within the .filter() function?
You could take a default value.
Either directly
(item.description || '').toLowerCase().includes(searchQuery)
or take a destructuring with a default values.
results = myArray.filter(({ title = '', description = '' }) =>
title.toLowerCase().includes(searchQuery) ||
description.toLowerCase().includes(searchQuery)
);
You can do something like this:
const myArray = [
{
title: 'Test 123',
},
{
title: 'Test 123',
description: 'test',
},
{
title: 'What',
},
{
title: 'Fix',
description: '456123',
},
];
const search = (text) => {
let results = myArray.filter(item => {
if (item.description) {
return item.title.toLowerCase().includes(text) || item.description.toLowerCase().includes(text);
}
});
return results;
}
const results = search('test');
console.log(results);
search(searchQuery: string) {
return myarray.filter(item => 'description' in item
? item.description.toLowerCase().includes(searchQuery)
: item.title.toLowerCase().includes(searchQuery)
);
}
Might want to do the same check for title too but how far down do you go.
I was wondering about how to replace the item if it already exists inside state array and I got a great solution
https://codesandbox.io/s/4xl24j7r69
It works fine but my problem is I can't add a new item if the item doesn't exist inside the array I got an error Cannot convert undefined or null to object
Like this:
add = () => {
let newUser1 = {
"userId": 1,
"id": 3, // this is new id which is doesn't exist in this.state.data
"title": "Two New",
"body": "new data"
}
this.setState(prevState => {
let newData = prevState.data;
let user = newData.find(d => d.id === newUser1.id);
Object.assign(user, newUser1);
return { data: newData };
})
};
You're not adding the newUser to the data on state.
add = () => {
let newUser1 = {
"userId": 1,
"id": 4,
"title": "Two New",
"body": "new data"
}
this.setState(prevState => {
const user = prevState.data.find(d => d.id === newUser1.id);
if (!!user) {
//Casting user to a boolean, if find did return a truthy value you add it to the data with map
Object.assign(user, newUser);
const newData = prevState.data.map(d => d.id === user.id ? user : d);
return { data: newData }
} else {
return { data: prevState.data.concat(newUser1)};
}
})
};