I seem to be having a problem with indexOf to get an exact match.
I have an API that searches for items in MongooseDB where the search query matches. This works only to some extend.
Here is the used code:
exports.searchItems = (req, res) => {
console.log(req.query.searchQuery);
if (!req.query || !req.query.searchQuery) {
return res.status(400).send({
message:"'searchQuery' param should be passed with valid query!"
});
}
Item.find().then(itemsList => {
if(!itemsList) {
return res.status(404).send({
message: "No Items found for search query: " + req.query.searchQuery
});
}
var items = [];
const queryVal = req.query.searchQuery;
itemsList.forEach(function(item){
if(item) {
if(JSON.stringify(item).indexOf(queryVal) > -1) {
items.push(item);
}
}
});
res.status(200).send({items});
}).catch(err => {
res.status(500).send({
message: err.message || "Some error occured while fetching all items!"
});
});
};
Now let's say the search query is "LO_Buehne2" the result will be:
{"items":[{"_id":"5f9fcf321337250c6815ac47","ort":"LO_Buehne2","bezeichnung":"ABB Beleuchtungsstärkemessgerät","seriennummer":"H42805104","pruefdatum":"01.01.2021","ausgeliehen":false,"ausleih_datum":"","ausleih_name":"","ausleih_nummer":"","rueckgabe_datum":"","einsatzort":"","notiz":"","url":"http://192.168.1.48/fileserver/uploads/asfgasfasf.jpg","createdAt":"2020-11-02T09:19:46.287Z","updatedAt":"2021-03-19T10:41:45.591Z","__v":0}]}
This is correct since there is only one item with "ort":"LO_Buehne2"
But if I search for LO_Buehne the result will be this:
{"items":[{"_id":"5f8fe281ee35da3068b0ac49","ort":"LO_Buehne","bezeichnung":"Panasonic Akku- u. Schlagschrauber","seriennummer":"9090442 / 9080163","pruefdatum":"01.01.2021","ausgeliehen":false,"ausleih_datum":"","ausleih_name":"","ausleih_nummer":"","rueckgabe_datum":"","einsatzort":"","url":"http://192.168.0.65/fileserver/3.jpg","createdAt":"2020-10-21T07:25:53.855Z","updatedAt":"2021-03-19T10:37:17.774Z","__v":0,"notiz":""}
...
...
...
{"_id":"5f9fcf321337250c6815ac47","ort":"LO_Buehne2","bezeichnung":"ABB Beleuchtungsstärkemessgerät","seriennummer":"H42805104","pruefdatum":"01.01.2021","ausgeliehen":false,"ausleih_datum":"","ausleih_name":"","ausleih_nummer":"","rueckgabe_datum":"","einsatzort":"","notiz":"","url":"http://192.168.1.48/fileserver/uploads/asfgasfasf.jpg","createdAt":"2020-11-02T09:19:46.287Z","updatedAt":"2021-03-19T10:41:45.591Z","__v":0}
As you can see, it finds all items with "ort":"LO_Buehne" but it will also find that one item with "ort":"LO_Buehne2".
Question is...why? And how do I fix this?
If you're looking for an exact match you have two solutions.
Regex magic
Work with javascript objects and === instead of JSON strings.
1. Regex Solution
const search = 'LO_Buehne';
const reg = new RegExp(`"${search}"`);
console.log(reg.test('{opt: "LO_Buehne2"}')); // fail
console.log(reg.test('{opt: "LO_Buehne"}')); // works
This is simple regex that looks to see if the search term you're looking for is surrounded by " (double quotes). This will only work for values in double quotes.
2. Working with objects
const data = {"items":[{"_id":"5f9fcf321337250c6815ac47","ort":"LO_Buehne2","bezeichnung":"ABB Beleuchtungsstärkemessgerät","seriennummer":"H42805104","pruefdatum":"01.01.2021","ausgeliehen":false,"ausleih_datum":"","ausleih_name":"","ausleih_nummer":"","rueckgabe_datum":"","einsatzort":"","notiz":"","url":"http://192.168.1.48/fileserver/uploads/asfgasfasf.jpg","createdAt":"2020-11-02T09:19:46.287Z","updatedAt":"2021-03-19T10:41:45.591Z","__v":0},{"_id":"5f8fe281ee35da3068b0ac49","ort":"LO_Buehne","bezeichnung":"Panasonic Akku- u. Schlagschrauber","seriennummer":"9090442 / 9080163","pruefdatum":"01.01.2021","ausgeliehen":false,"ausleih_datum":"","ausleih_name":"","ausleih_nummer":"","rueckgabe_datum":"","einsatzort":"","url":"http://192.168.0.65/fileserver/3.jpg","createdAt":"2020-10-21T07:25:53.855Z","updatedAt":"2021-03-19T10:37:17.774Z","__v":0,"notiz":""}]}
const search = 'LO_Buehne';
const res = data.items.filter(item => {
return new Set(Object.values(item)).has(search);
});
console.log(res);
This solution consists in doing a strict equal to see if the value exists. In the above example I use a combination of Object#values and Set, but it's completely doable with Array#find / Array#findIndex.
i.e.:
const res = data.items.filter(item => {
return Object.values(item).findIndex(value => value === search) > -1;
});
Related
Right now my code is looking for the words 'Cheese' or 'Bread' within a specific webpage, and if it finds either word it should display an alert. However, it only displays the alert if the first word is found (cheese). Any suggestions on how to fix it so that it will successfully look for more than one word?
var array = Array.from(document.getElementsByClassName('wide-content-host'))
.find(el => el.innerText.includes('Cheese', 'Bread'));
if (array){
alert("Word found!")
}
This is an obvious change, but we could put an OR operator inside of the statement to signify both of them, like so:
let array = Array.from(document.getElementsByClassName('wide-content-host'))
.find(el => el.innerText.includes('Cheese') || el.innerText.includes('Bread'));
if (array) alert('Word found!');
You could also do it a more elegant way, like so:
const conditions = ['Cheese', 'Bread'];
const array = Array.from(document.getElementsByClassName('wide-content-host'));
const results = array.find((el) => conditions.some(nEl => el.innerText.includes(nEl)));
if (results) alert('Word found!');
This one works by grabbing the array from the 'wide-content-host' class name, then looping through that array with another loop that is looping through the values of the conditions array. With all of these loops working together, it will check whether or not the elements include the conditions.
** Edit **
In order to make the methods work without case-sensitivity, you would need to make the search cases lowercase e.g. 'cheese' and 'bread', and you would need to make the strings that you are searching through completely lowercase also.
Here are the examples for case-insensitivity:
let array = Array.from(document.getElementsByClassName('wide-content-host'))
.find(el => el.innerText.toLowerCase().includes('Cheese') || el.innerText.toLowerCase().includes('Bread'));
if (array) alert('Word found!');
or
const conditions = ['cheese', 'bread'];
const array = Array.from(document.getElementsByClassName('wide-content-host'));
const results = array.find((el) => conditions.some(nEl => el.innerText.toLowerCase().includes(nEl)));
if (results) alert('Word found!');
This can be done with regular expressions
let elem = document.querySelector("section");
let entries = elem.innerHTML.match(/(cheese)|(bread)/gi);
if (entries.length > 0) {
alert(`Words were found: ${entries}`);
}
<section>
<p>Cheese Bread</p>
<p>cheeSE BREAD</p>
</section>
I have an array of strings. I need to grab the URL's value on each string. I have created a function with Regex to accomplish this. It works fine, but there's an edge case I haven't been able to cover. When there is not any URL:'s value, I get the error: Cannot read property '1' of null.
This is my code:
const arrStr = [
`describe
url: https://url-goes-here-/1,
https://url-goes-here-/2,
https://url-goes-here-/3
});`
,
`
before(() => {
url: https://url-goes-here-/4
});
`,
`
before(() => {
url: https://url-goes-here-/5
});
`,
`describe
// nothing http link here
});
`
]
const getXrayUrl = str => str.match(/url:([^;]+)/)[1].trim().split('(')[0]; // cannot read property '1' of null
const allXrayUrls = arrStr.map(item => getXrayUrl(item));
If I removed the string with no URL value from the array, I get this output:
[ 'https://url-goes-here-/1,\n https://url-goes-here-/2,\n https://url-goes-here-/3\n })',
'https://url-goes-here-/4\n })',
'https://url-goes-here-/5\n })' ]
How do I cover this edge case & return another array with all the string in the level?
According to match function documentation, it returns an array of matches, or null if no matches are found.
If you need to handle the case of a missing URL attribute so check match array is null before accessing the capture group as following:
const match = str.match(/url:\s*([^;]+)\n/)
// in case no match retrun empty string
// split the match on , to handle multi URL case
const url = match? match[1].split(",").map(item => item.trim()) : [""];
after that filter match results removing empty values as following:
arrStr.map(getXrayUrl).flat().filter(item => item !== "");
so final solution as following:
const arrStr = [
`describe
url: https://url-goes-here-/1,
https://url-goes-here-/2,
https://url-goes-here-/3
});`
,
`
before(() => {
url: https://url-goes-here-/4
});
`,
`
before(() => {
url: https://url-goes-here-/5
});
`,
`describe
// nothing http link here
});
`
]
const getXrayUrl = str => {
const match = str.match(/url:\s*([^;]+)\n/)
// in case no match retrun empty string
// split the match on , to handle multi URL case
return match? match[1].split(",").map(item => item.trim()) : [""];
}
const allXrayUrls = arrStr.map(getXrayUrl).flat().filter(item => item !== "");
console.log(allXrayUrls)
console output:
["https://url-goes-here-/1", "https://url-goes-here-/2", "https://url-goes-here-/3", "https://url-goes-here-/4", "https://url-goes-here-/5"]
I'm building out a function to handle dynamic phone number swapping, identifying the referral url, mapping through a data set, and then outputting the promoUrl's related phone number. The data is formatted like this:
const phoneNumbers = [
{
promoUrl: '/interior-doors/',
promoNumber: '589-918-0710',
},
{
promoUrl: '/promo4/',
promoNumber: '307-789-8615',
},
];
And the function maps through the data and reduces it, allowing me to sort through it like this:
const url = location.pathname.replace(/\/+$/, '');
const promoNumber = phoneNumbers.reduce((promoNumber, results) => {
const hasPromo = results.promoUrl.includes(url);
if (hasPromo) {
return results.promoNumber;
}
return promoNumber;
}, '');
I'm having some issues with hasPromo. The way it's built right now allows for the promoUrl to have some variance and still work, meaning as long as it includes what is returned from url then it works i.e. /interior-doors/, /interior-doors, and interior-doors will all work, which is great, but it also works if url is /interior-do. I need it to not do that. It should work independent of formatting, but only return the promoNumber if the string of letters is exact.
I'm assuming this is a regex thing, but I don't know what I'm doing with regex. Any help would be greatly appreciated.
Use == to do an exact match. And since the promoURL property always has / around it, add those delimiters when you set url
const url = '/' + location.pathname.replace(/\/+$/, '') + '/';
const promoNumber = phoneNumbers.reduce((promoNumber, results) => {
const hasPromo = results.promoUrl == url;
if (hasPromo) {
return results.promoNumber;
}
return promoNumber;
}, '');
reduce also seems like the wrong function for this. You just need to find the matching promoUrl, you don't have to continue reducing, since the reduction function doesn't merge the results in any way.
const promo = phoneNumbers.find(({promoUrl}) => promoUrl == url);
const promoNumber = promo ? promo.promoNumber : '';
Search results are not working while entering the details.
For Example i have a value "Google Search Result".
If I search "g", "go", "goo" ... "Google Search" or "Search Result" it is working fine.
But If I search "Google Result", without entering the middle word, it is not showing anything.
How can I achieve this in angularjs,
return cardDetails.filter(function (card) {
return (!$scope.search || ($scope.search && $scope.search.toLowerCase().split(' ').every(function(str){
return card.Tag.toLowerCase().indexOf(str) != -1;
})));
}).length > 0;
$scope.getAllCards.filter Instead of using filter method, how can I use angular "Array Some method"
In this code, search is working fine. But the only thing, I need to enter continiously, if I enter first and last, it is not showing anything. Only empty result is coming.
Can anyone help me to do this?
The some() method tests whether at least one element in the array passes the test implemented by the provided function. It returns a Boolean value.
In your case you need to use every function
The every() method tests whether all elements in the array pass the
test implemented by the provided function. It returns a Boolean value
Here is the working example
const searchValue = "Google Result";
const tags = ["Google", "Google Search Result", "Another Google Result","Result of Google"];
const searchArr = searchValue.split(' ');
const filteredTags = tags.filter(item => {
// loop searchArr array and test if current item includes all occurrence
return searchArr.every(el => item.includes(el));
})
console.log(filteredTags);
For angularjs it will like this
$scope.getTags = function () {
var searchArr = $scope.search.split(' ');
return $scope.getAllCards.filter(function(item) {
return searchArr.every(function(el){
return item.includes(el);
})
}
You could use Array.prototype.reduce() to split the array and search among the it for the search text and return if it exists or not as follows:
const getAllCards = [{
Tag: 'card1',
pillarid: '1'
}, {
Tag: 'card2',
pillarid: '7'
}, {
Tag: 'Google Search Result',
pillarid: '0'
}],
categoryId = 0;
getTags = function(search) {
return getAllCards.some(function(a) {
return a.Tag;
}) ? getAllCards.filter(function(card) {
return (!search || (search && search.split(' ').reduce(function(acc, str) {
return acc && (card.Tag.toLowerCase().indexOf(str.toLowerCase()) !== -1);
}, true) && card.pillarid != '7' && (categoryId == card.pillarid || categoryId == 0)));
}) : [];
}
document.getElementById('input').addEventListener('keyup', function() {
console.clear();
console.log(getTags(this.value));
})
<input id="input" />
PS: you don't need a .map() chained to .filter() as you've put in your example
So, I'm writing a client-side search and I need to look through strings of Japanese characters. I'm wondering how to do this properly?... i.e. Do I change the format of the text into utf-8 something and then search the utf-8?
Example:
All my data has japaneseData.title : "フェリーチェ三田"
When I type in my search.value as : "フェ" using japaneseData.title.includes(search.value) I don't get a match...
How do I do this correctly?
Okay, after further inspection, the comments were correct and includes was finding the substring. This is all happening inside of a filter() and I'm trying to return the objects that match...
After changing my code to:
let filteredArrayofObjects = Lists.houseLists.filter(house => house.building_name.includes(query.search));
I was getting back some but not all. Problem cases:
"アーバイルスパシエ芝浦BAY-SIDE".includes("エ芝浦"); // this evaluates to true, but does not get included in my filtered array...
Okay, further digging, it seems the issue is I need to wait for the filter process before returning the results... haven't yet found a solution to that just yet.
async filter(arr, callback) {
return (await Promise.all(
arr.map(async item => {
return (await callback(item)) ? item : undefined;
})
)).filter(i => i !== undefined);
}
handleFilterLists = async (query = {}) => {
const { Lists } = this.props;
let searchResults = await this.filter(Lists.houseLists, async house => {
return house.building_name.includes(query.search);
// the final evaluation to look similar to this:
// var newArray = homes.filter(function (el) {
// return el.price <= 1000 &&
// el.sqft >= 500 &&
// el.num_of_beds >=2 &&
// el.num_of_baths >= 2.5;
// });
});
this.setState({ searchResults });
}
Okay, so, I'm trying to set state.searchResults after the filter method has checked for matching objects in the array Lists.houseLists...
includes returns true or false if the substring is detected or not. If you want the index of where the first detected substring begins, use indexOf.
I used your sample source and search text with includes and it returns true.
Edit:
I used your updated data and this still works. https://codepen.io/anon/pen/RMWpwe
const sourceText = 'アーバイルスパシエ芝浦BAY-SIDE';
const searchText = 'エ芝浦';
const lists = [
'スパシエ',
'芝浦BAY-SIDE',
'エ芝浦',
'パシエ芝浦BAY'
];
console.log(lists.filter(item => item.includes(searchText)));
// ["エ芝浦", "パシエ芝浦BAY"]