I have JSON objects of chat messages in messages.json, as seen below:
[
{
"sender": "Bob",
"text": "Hi, how are you?"
},
{
"sender": "James",
"text": "Good, you?"
}
]
I want to search messages for the index of a term received from a text input. However when I run the following code, I receive "undefined" in the console.
const handleChange = (event) => {
const result = messages.filter((message) => {
return message.text.indexOf(event.target.value);
});
console.log(result);
};
Why is it returning undefined?
indexOf returns 0 if it found at the first character, which evaluates to FALSE. Try it with
return message.text.indexOf(event.target.value) > -1
indexOf() returns 0 based index so you need to convert it to boolean to avoid false positives. Also event.target.value might be empty, or has a different case.
I recommend making sure both values are defined and converting both strings to same case toLowerCase() before comparison
const messages = [{
"sender": "Bob",
"text": "Hi, how are you?"
},
{
"sender": "James",
"text": "Good, you?"
}
]
const result = messages.filter((message) => {
return message.text.toLowerCase().indexOf("good") >= 0;
});
console.log(result);
Since message is array, indexOf won't work, you need to iterate to find single msg, use find() for that, to find multiple instances use filter()
const handleChange = (event) => {
const result = message.find(i=>i.text.toLowerCase().indexOf(event.target.value.toLowerCase()));
console.log(result);
};
Related
I have some data (json) that looks like this:
[
{
"name": "Enterprise Networking",
"technology": "Networking"
},
{
"name": "Enterprise Networking",
"technology": "Networking"
},
{
"name": "Wireless Insights",
"technology": "Mobility and Wireless"
},
{
"name": "Mobility Insights",
"technology": "Mobility and Wireless"
},
{
"name": "Lock it down",
"technology": "Security"
},
{
"name": "Collaboration",
"technology": "Security"
}
]
I am trying to find matches based on an array of filtering options for one field named 'technology' in this example. For example, my filtering criteria will be ["Networking", "Security"]. I would expect to return all objects that have 'Networking' and 'Security' as their technology, excluding 'Mobility and Wireless' in this example.
I have figured out how to do it with just one filter criteria using:
result = learningMapsDataObj.filter(el => el.Technology === filter3[0].value);
I've tried adding a method that will loop through the the filter array, but can't get it to work.
result = learningMapsDataObj.filter(el => el.Technology === myloopingArrCheckFunction(el.technology, filters3));
I know I'm close, but haven't had to dev in a while and my multidimensional brain isn't working.
Approaches
You can check if el.technology is included in your array of values to filter by:
const allowedTechnologies = ["Networking", "Security"];
learningMapsDataObj.filter(el => allowedTechnologies.includes(el.technology))
Alternatively, you can also check if el.technology is like some of the values to filter by:
const allowedTechnologies = ["Networking", "Security"];
learningMapsDataObj.filter(el => { // Linebreak (thus function body) for readability
return allowedTechnologies.some(allowedTech => allowedTech === el.technology);
})
Map to string array
It seems your filter is in a form similar to this:
const filter = [
{ value: "Networking" }.
{ value: "Security" }
];
But it would be easier to use if it was in the form as shown in my examples above. You can achieve that by mapping the filter elements to their values:
const filter = [
{ value: "Networking" }.
{ value: "Security" }
];
const filterValues = filter.map(el => el.value);
// Now you can use filterValues like allowedTechnologies
let result = learningMapsDataObj.filter(el => el.technology === "Networking" || el.technology === "Security" );
You can filter the array with an array of criterias like this
const filterCriteria= ["Networking", "Security"]
const filteredArray = learningMapsDataObj.filter((item) => filterCriteria.includes(item.technology));
if you want to set a variable for fields you want to filter, you can also do something like this
const filterField = 'technology'
const filterCriteria= ["Networking", "Security"]
const filteredArray = learningMapsDataObj.filter((item) => filterCriteria.includes(item[filterField]));
I am having a hard time filtering through an array of objects based on a value in a nested array of objects. I have a chat application where a component renders a list of chats that a user has. I want to be able to filter through the chats by name when a user types into an input element.
Here is an example of the array or initial state :
const chats= [
{
id: "1",
isGroupChat: true,
users: [
{
id: "123",
name: "Billy Bob",
verified: false
},
{
id: "456",
name: "Superman",
verified: true
}
]
},
{
id: "2",
isGroupChat: true,
users: [
{
id: "193",
name: "Johhny Dang",
verified: false
},
{
id: "496",
name: "Batman",
verified: true
}
]
}
];
I want to be able to search by the Users names, and if the name exists in one of the objects (chats) have the whole object returned.
Here is what I have tried with no results
const handleSearch = (e) => {
const filtered = chats.map((chat) =>
chat.users.filter((user) => user.name.includes(e.target.value))
);
console.log(filtered);
// prints an empty array on every key press
};
const handleSearch = (e) => {
const filtered = chats.filter((chat) =>
chat.users.filter((user) => user.name.includes(e.target.value))
);
console.log(filtered);
// prints both objects (chats) on every keypress
};
Expected Results
If the input value is "bat" I would expect the chat with Id of 2 to be returned
[{
id: "2",
isGroupChat: true,
users: [
{
id: "193",
name: "Johhny Dang",
verified: false
},
{
id: "496",
name: "Batman",
verified: true
}
]
}]
The second approach seems a little closer to what you're trying to accomplish. There's two problems you may still need to tackle:
Is the search within the name case insensitive? If not, you're not handling that.
The function being used by a filter call needs to return a boolean value. Your outer filter is returning all results due to the inner filter returning the array itself and not a boolean expression. Javascript is converting it to a "truthy" result.
The following code should correct both of those issues:
const filtered = chats.filter((chat) => {
const searchValue = e.target.value.toLowerCase();
return chat.users.filter((user) => user.name.toLowerCase().includes(searchValue)).length > 0;
});
The toLowerCase() calls can be removed if you want case sensitivity. The .length > 0 verifies that the inner filter found at least one user with the substring and therefore returns the entire chat objects in the outer filter call.
If you want to get object id 2 when entering bat you should transform to lowercase
const handleSearch = (e) =>
chats.filter(chat =>
chat.users.filter(user => user.name.toLowerCase().includes(e.target.value)).length
);
try this it should work
const handleSearch2 = (e) => {
const filtered = chats.filter((chat) =>
chat.users.some((user) => user.name.includes(e))
);
console.log(filtered);
};
filter needs a predicate as argument, or, in other words, a function that returns a boolean; here some returns a boolean.
Using map as first iteration is wrong because map creates an array with the same number of elements of the array that's been applied to.
Going the easy route, you can do this.
It will loop first over all the chats and then in every chat it will check to see if the one of the users' username contains the username passed to the function. If so, the chat will be added to the filtered list.
Note, I am using toLowerCase() in order to make the search non case sensitive, you can remove it to make it case sensitive.
const handleSearch = (username) => {
var filtered = [];
chats.forEach((chat) => {
chat.users.forEach((user) => {
if (user.name.toLowerCase().includes(username.toLowerCase())) {
filtered.push(chat);
}
});
});
console.log(filtered);
return filtered;
}
handleSearch('bat');
I am trying to search an array for query string on a json object. The returned fields are an array of filtered results. Currently, the list is returning the name field but not the number field.
computed: {
search: function () {
let self = this
let filtered = []
filtered = self.jcontacts.filter(function(contact){
return contact.firstname.toLowerCase().indexOf(self.query.toLowerCase())>=0 ||
contact.lastname.toLowerCase().indexOf(self.query.toLowerCase())>=0;
contact.email.toLowerCase().indexOf(self.query.toLowerCase()) >=0 ||
contact.phonenumber.toLowerCase().indexOf(self.query.toLowerCase()) >=0;
}
);
return this.contacts = filtered
}
},
}
The filtered method in the search method is not showing the number. An example of the json is below:
[
{
"id": 1,
"phoneNumber": [
"3908902"
],
"email": [
"jamie#fox.com"
],
"firstname": "Jamie",
"lastname": "Fox"
}]
Beware of case phoneNumber != phonenumber
phoneNumber is stored as array not string, so you cant look for it like that (use .includes() function for example)
For code formatting consider to store self.query.toLowerCase() as variable or another computed property
It is a typo. Check out field phoneNumber is filtered as phonenumber.
It is an array so you can do it as,
contact.phoneNumber.forEach( number => {
if(number.toLowerCase().indexOf(self.query.toLowerCase()) >=0)
return true;
});
I think similarly you can write it down for email as well because it is also an array.
I am running a JSON server to create a simple login system. The data from the server looks like this:
{
"users": [
{
"name": "user1",
"password": "pass1",
"email": "useer1#user.com",
"id": 2,
"details": {
"first_name": "user1_1",
"last_name": "user1_2",
"gender": "male",
"pic": "",
"about": "fnbewhbdwie"
}
},
{
"name": "user2",
"password": "pass2",
"email": "user2#user.com",
"details": {
"first_name": "user2_1",
"last_name": "user2_2",
"gender": "male",
"pic": "",
"about": "cjkdbvcwvebxcnoewbvcu"
},
"id": 4
}
]
}
I have created a function to check if the name and password match from the user input, and if the condition is true I want to run another function that will return only the details of the object that have the name and password from the user input.
I have tried the find() method but it returns the details for the first object only
async getAcc() {
const {data} = await Axios
.get(`${BASE_URL}users`)
data.forEach(({name, password}) => {
if(this.nameInput.value === name && this.passInput.value === password){
showAcc();
}
else {
return false
}
}
)
function showAcc() {
let result = data.find(a => a.details)
console.log(result)
}
}
Putting aside the obvious problem with the user/pwd on the response, as mentioned on comments, I believe that what is wrong here is this:
First, your data is an array of objects.
So, by definition, 'find' will return the first entry that the lamba function successfully satisfies. In this case, you are passing the array into the find and saying, give me the first details object you find. It is doing the right thing.
I believe that your problem is on the forEach. Since you added name and pwd as parameters , it is only picking those properties for each item on the array. Try and change it to something like
.forEach(item => {,
which will send into the lamba function the complete object. Then, inside the method you will need to modify and use
if(this.nameInput.value === item.name
Finally, inside your if statement, you'll be able to even simplify your showAcc by sending the "details" property straight into the method like:
showAcc(item.details);
and
function showAcc(details) {
console.log(details);
}
Would be better if you had a sample we could edit , but I think this should work.
Good luck
I'd use the lodash filter method instead of find method.
Something like: _.filter(data, { 'name': 'user2', 'password': 'pass2' });
It should get you all the objects that match your criteria passed in the argument object as shown above.
WebDever may have pointed you to a better solution. I'll just point out my observation, which directly explains why you fail to print all the answers.
I don't know anything about any of the technology used, but it seems that the problem has to be the return value (or lack thereof) of the code block inside the forEach. Just showing the result, it appears, should in and of itself have no side effect in terms of stopping the iteration. So the key thing that happens when you first find a match is that you don't return 'false'. Up to that point, you've always been returning 'false'.
So my guess is that when you find a match, you're returning the result of the last expression rather than 'false', and that return value causes the iteration to stop. So change the code within the foreach to:
if(this.nameInput.value === name && this.passInput.value === password){
showAcc();
}
return false
I have an object containing multiple other objects, inside these nested objects is an array containing multiple objects, each with a uid. I'm trying to loop over the objects and find the object that contains a particular uid.
My data looks like this
const data = {
"3c5671fde44f44f9ad59d59eb810d87e": {
"heading": "Heading 1",
"items": [
{
"content": {
"uid": "4fcd5f4af7a448d48463d4e0a11297d9"
}
},
{
"content": {
"uid": "31f975440a0a431592e127b2891cd142"
}
}
]
},
"ea80e8315b554c9bb40958a6cacf4b0c": {
"heading": "Heading 2",
"items": [
{
"content": {
"uid": "d6de8db4c2a74da6915a44d3964277d6"
}
}
]
}
}
The uid I want to search for is d6de8db4c2a74da6915a44d3964277d6 when found I want to return it's parent object so I can access the heading property.
Current code looks like this but it doesn't work, it's been a long day so I'm likely missing something really simple.
const currentUid = "d6de8db4c2a74da6915a44d3964277d6";
const currentHeading = Object.keys(data).forEach(section => {
return data[section].items.filter(item => {
return item.content.uid === currentUid;
});
});
When debugging it successfully evaluates to true when it finds the correct uid, it just doesn't return anything.
Any help welcome!
forEach is meant just for looping the array, use find to find the key of your object from Object.keys(data). And inside the callback use some instead of filter to check the existance. This solution will result in either the key of the object or null. To get the object, just check that a key is returned and then use that key to get the object:
const currentHeadingKey = Object.keys(data).find(section => {
return data[section].items.some(item => {
return item.content.uid === currentUid;
});
});
const currentHeading = currentHeadingKey != null ? data[currentHeadingKey] : null;
currentHeading is now either the whole object if found, null otherwise. You can access the heading property of that object.
Note: Since the callbacks of both find and some have only one statement in them, you can use the implicit return of arrow function to shorten the code:
const currentHeadingKey = Object.keys(data).find(section =>
data[section].items.some(item => item.content.uid === currentUid)
);
Consider using the Object.values() method instead, to extract the "parent heading" for the supplied uid.
Taking this approach, you can iterate the values of data, and filter those section values that contain items matching the uid. In the answer below, this is done via:
return items.some(item => item.content.uid === currentUid)
Finally, you can map() the filtered sections to acquire the corresponding heading(s) of section with matching uid items:
function findHeading(data, uid) {
return Object.values(data).filter(section => {
// Find any item with matching uid in this section, filter this section accordingly
return section.items.some(item => item.content.uid === currentUid)
})
.map(section => {
// Map heading from any sections matching item uid
return section.heading
});
}
const data = {
"3c5671fde44f44f9ad59d59eb810d87e": {"heading": "Heading 1","items": [{"content": {"uid": "4fcd5f4af7a448d48463d4e0a11297d9"}},{"content": {"uid": "31f975440a0a431592e127b2891cd142"}}]},"ea80e8315b554c9bb40958a6cacf4b0c": {"heading": "Heading 2","items": [{"content": {"uid": "d6de8db4c2a74da6915a44d3964277d6"}}]}
}
const currentUid = "d6de8db4c2a74da6915a44d3964277d6";
console.log(findHeading(data, currentUid))