Advanced search in Javascript - javascript

I try to implement a search bar in a project.
It should just search for the entries and hide the rows, when the input doesn't match.
This is what I did and it's working.
_searchCalls() {
let rows = document.querySelector('call-journal').shadowRoot.querySelectorAll('#row')
let callEntries = document.querySelector('call-journal').shadowRoot.querySelectorAll('call-entry')
for(let i = 0; callEntries.length > i; i++) {
let tmp = callEntries[i].shadowRoot.querySelector('.callInfo')
let entries = tmp.textContent.toLowerCase()
let dates = callEntries[i].shadowRoot.querySelector('.dateWrapper').textContent
let userInput = this._getSearchInput().toLowerCase()
if(!(entries.includes(userInput) || dates.includes(userInput))) {
rows[i].classList.add('hide')
} else {
rows[i].classList.remove('hide')
}
}
}
I want to extend the search. So what I write 'Bill Gates' it works, but not when I write 'Gates Bill'.
Any help would be much appreciated.

Reverse your logic.
Instead of telling each row to show/hide,
make each row listen to the change/keyup event on the search box.
Yes, that means an addEventListener for every row.
Search: <input type="text" value="foo bar">
<row-item>qux, baz, foo, bar</row-item>
<row-item>corge, foo</row-item>
<row-item>baz, quuz, bar, quux, foo</row-item>
<row-item>baz, corge, bar, quuz</row-item>
<row-item>bar</row-item>
<row-item>corge, baz, quux</row-item>
<row-item>baz, corge</row-item>
<row-item>foo</row-item>
<row-item>bar, quux, corge, foo</row-item>
<style>
row-item { display: block }
</style>
<script>
customElements.define("row-item", class extends HTMLElement {
connectedCallback() {
document.querySelector("input")
.addEventListener("keyup",(evt) => this.match(evt.target.value));
this.match("foo bar"); // for testing!
}
match(search) {
let findWords = search.trim().split(" ");
let rowWords = new Set(this.innerHTML.split(/, /));
let matched = findWords.map(word => rowWords.has(word))
.filter(Boolean) // remove false values
.length == findWords.length;
this.style.backgroundColor = matched ? "lightgreen" : "lightcoral";
}
})
</script>

Like the first comment from suggested, it sounds like you are trying to match all words in the search input to the rows/entries.
First, to break the input into an array of multiple terms, you could use the String method .split(' ') to split on spaces. For example:
"Bill Gates".split(' ')
This would result in an array that looks like ['Bill', 'Gates']
Then, you could loop through the array of search terms you created with .split() and check if they exist in a row/entry with the String .includes() method (like you're checking right now on the userInput string).

Related

Forbidden words checker

i'm new on stack but here's my question:
I'm trying to check if an html input's value has one of my forbidden words that are in a array.
I've tried:
Using split(), transforming the input.value in a array and then comparing each other.
Using toString() to transform the array into a string, and then comparing both.
Using a string with all the words ("wordone wordtwo wordthree") instead an array(["wordone","wordtwo", "wordthree"]).
Spliting the string above then comparing.
And some variants of the examples above, but always checking with includes() method.
I'm really not understanding why nothing works.
When i put only one forbidden word in the array, it works.
If i write more than one word on the input element, even if there's only one word in the array, it not works.
Here's the HTML:
<input id="field" type="text" size="100">
<button id="check">check</button>
<p id="announce">Message</p>
Here's the JS:
fieldIncludes() {
let field = document.querySelector('#field'); //gets the field
let btn = document.querySelector('#check'); //gets the check-field button
let messageBox = document.querySelector('#announce'); // gets the <p> to show a message
let forbiddenWords = ['wordOne', 'wordTwo', 'wordThree'];
btn.addEventListener("click", () => {
if (field.value.includes(forbiddenWords)){
messageBox.innerHTML = "Forbidden!";
} else {
messageBox.innerHTML = "Okay :)";
}
})
}
Please ignore that the JS is in a class.
hope i didn't get you wrong.
var str = 'you plays like a noob';
function inspect_string(str, arr){
flag = false;
arr.forEach(ar=>{
flag = str.match(new RegExp(ar)) ? true : false;
});
return flag
}
// var arr = ['noob', 'trash'];
var arr = ['nice', 'good'];
console.log(inspect_string(str, arr));
The argument to includes() should be a single string to check, it doesn't automatically loop over the array. You can use the .some() method to check if any of them match.
if (forbiddenWords.some(word => field.value.includes(word))) {
messageBox.innerHTML = "Forbidden!";
} else {
messageBox.innerHTML = "Okay";
}

How to check if a keyword is present regardless of capitalization?

I am writing a program that looks for specific keywords and then only performs an action when a keyword is present in a message. I am having difficulty trying to make it so that it will pickup on keywords regardless of their capitalization. Below is an example of what I currently have.
for (var i = 0; i < keyword.length; i++) {
if (msg.content.includes (keyword[i])) {
msg.channel.send("Orange")
}
var keyword = ["Apple","Banana"]
The only way I've figured out how to do this is to add each variation to the keyword list. How would I make it so that it could detect for example "apple" or "BaNaNa" without adding those variations to the keyword list?
If your message is a string, just make the whole thing lower case and match it to lowercase keywords.
let message = msg.content.toLowerCase();
if (message.includes(keyword[i].toLowerCase()) {
....
}
Convert to lowercase both, the database and the search terms.
You can convert both your message and keyword to lowercase and check the existing keyword in your message.
if (msg.content.toLowerCase().includes (keyword[i]).toLowerCase()) {
msg.channel.send("Orange")
}
If I have a big string, 'Hello Marco, how are you doing?', and I have another string, 'MarCo', and I want to check if the second string is in the first, I would do
const needle = 'MarCo';
const haystack = 'Hey Marco, how are you doing?'
const inc = haystack.toLowerCase().includes(needle.toLowerCase());
if (inc) {
console.log('its in there')
} else {
console.log('not in there');
}

How to check text input changes for certain words?

I need to create a web page on which I have added a text field. I have set an ID to the text field (say, 'custom'). Now, I want to modify the webpage if the text field contains certain words.
For example, if the text field contains a word, say apple, I want to do something.
If the text field contains ball, I want to do something else.
And so on.
I had found this:
<script>
var check=document.getElementById("custom").value == "text_value";
//check will be true or false
if (check){ //do something if true}
if(!check){//do something if false}
</script>
I don't even know if it's correct or not, but, I realised, it won't be able to manage multiple conditions. Because, if the text contains apple, it won't contain ball, it will create wierd behaviour.
So, how can I achieve this?
Check out the following Vanilla JavaScript (plain JavaScript) example.
I.e. enter bla bla apple and you will get the expected result.
function doSomething() {
var text = document.getElementById("myInput").value;
if (text.includes("apple")) {
console.log("do something special because text contains apple");
}
else if (text.includes("ball")) {
console.log("do something special because text contains ball");
}
else {
console.log("text contains no special word");
}
}
Enter text: <input type="text" id="myInput" onkeyup="doSomething()">
Can you please run the code snippet below.
The trigger is onkeyup and was selected for demonstration purposes. You can change it as per your requirement, for example, run the javascript function watchWords upon pressing a button.
function watchWords()
{
var watch_words = ['apple', 'lemon', 'watermelon'];
var textvalue = document.getElementById('name').value;
for(var i=0; i<watch_words.length; i++) {
if (~textvalue.indexOf(watch_words[i])){
if(watch_words[i] == 'apple'){
console.log('Apple was found');
}
if(watch_words[i] == 'lemon'){
console.log('Lemon was found');
}
if(watch_words[i] == 'watermelon'){
console.log('Watermelon was found');
}
}
}
}
<input type="text" name="name" id="name" onkeyup="watchWords()" />
One option is to use an object indexed by the .values you want to identify, whose property values are the functions you you want to run for each, eg:
const objOfFns = {
apple() {
console.log('Apple!');
},
ball() {
console.log('Ball!');
bounceBall();
}
};
const { value } = document.getElementByid('custom');
const possibleFn = objOfFns[value];
// check hasOwnProperty to avoid accidentally referencing Object.prototype methods
if (possibleFn && objOfFns.hasOwnProperty(value) {
possibleFn();
}
Using jQuery on keyup event listener and includes function:
$("#custom").on("keyup", function() {
if (this.value.includes("apple")) {
// do something
}
});
The code above will check the custom input each time the user presses a key and execute the if block if the input string contains the word apple.
<script>
function common(arr1, arr2) {
var newArr = [];
newArr = arr1.filter(function(v){ return arr2.indexOf(v) >= 0;})
newArr.concat(arr2.filter(function(v){ return newArr.indexOf(v) >= 0;}));
return newArr;
}
var string = document.getElementById("custom").value;
var items = string.split(" ");
var yourlist = ["apple", "banana", "tomato"];
var intersection = common(yourlist, items);
for(i=0; i<intersection.length; i++){
console.log(intersection[i]);
var z = yourlist.indexOf(intersection[i]);
/* Your Logic Goes Here.. */
if(z == 0){
/* do something */
}else{
/* do something else */
}
}
</script>
Function Credits - https://gist.github.com/IAmAnubhavSaini

How to replace multiple keywords in a string with a component

To start off, I'm primarily an AngularJS developer and recently switched to React, and I decided to convert an angular webapp I had previously developed to a react app. Im having a bit of an issue with a component ExpressiveText that searches through a string for a match to a property on a list objects and inserts a component TriggerModal in its place that when clicked triggers a modal with more detailed information. So the properties passed into ExpressiveTest are: text, tags, and tagsProperty.
text is a string (i.e. "My search string")
tags is an array of objects (i.e. [{id: 1, name: 'my', data: {...}}, {id: 2, name: 'string', data: {...}}]
tagsProperty is the name of the property to search for as a "tag" (i.e. name)
I followed along with this issue to try and formulate an idea of how to approach this. The reason I mention that I am coming from angular is because the component I had previously created simply used something like text.replace(regex, match => <trigger-modal data={tags[i]} />) and then used angulars $compile function to render components in the text. This does not seem to be possible using react. This is what I have tried inside of my ExpressiveText component:
class ExpressiveTextComponent extends React.Component {
constructor (props) {
super(props);
this.filterText = this.filterText.bind(this);
}
filterText () {
let text = this.props.text;
this.props.tags.map(tag => {
const regex = new RegExp(`(${tag[this.props.tagsProperty]})`, 'gi');
let temp = text.split(regex);
for(let i = 1; i < temp.length; i+=2){
temp[i] = <TriggerModal data={tag} label={tag[this.props.tagsProperty]} />;
}
text = temp;
});
return text;
}
render () {
return (
<div className={this.props.className}>{this.filterText()}</div>
);
}
}
This works for the first tag. The issue with it is that once it goes to map on the second tag, text is then an array. I tried adding in a conditional to check if text is an array, but then the issue becomes that the text array becomes nested and doesnt work on the next iteration. Im having a really hard time wrapping my mind around how to handle this. I have also tried dangerouslySetInnerHTML using text.replace(...) but that doesn't work either and just renders [object Object] in place of the component. Any help or advice is much appreciated, I have to say this is probably the only major issue I have come across since my switch to React, otherwise its been very straightforward.
Edit: Since I had a question asking for expected output with a given input and more clarification, what I am looking for is a component that is given this input:
<ExpressiveText text="my text" tags={{id: 1, name: 'text'}} tagsProperty="name" />
would render
<div>my <TriggerModal label="text" data={...} /></div>
with a functional TriggerModal component.
If I am correct in my understanding of what you're trying to accomplish, this is one way to do this it. My apologies if I misunderstood your question. Also, this is pseudocode and I'll try and fill it in with real code in a bit. Sorry if this is difficult to understand, let me know and I will try to clarify
filterText () {
let text = [this.props.text];
for (let item in this.props.tags) {
//item will be something like {id: 1, name: 'text'}
let searchString = new RegExp(item.name, 'gi');
//loop through text array and see if any item matches search string regex.
while (text.some(val => val.test(searchString)) {
//if we are here, at least one item matches the regexp
//loop thru text array, and split any string by searchString, and insert <TriggerModal> in their place
for (let i = text.length-1; i >=0; i--) {
//if text[i] is string and it matches regexp, then replace with nothing
text[i].replace(searchString, "")
//insert <trigger modal>
text.splice(i, 0, <TriggerModal ... />)
}
//end of while loop - test again to see if search string still exists in test array
}
}
return text;
}
Looks like I found a solution.
filterText () {
let text = this.props.text.split(' '),
replaceIndexes = [];
if(this.props.tags.length > 0) {
this.props.tags.map(tag => {
const regex = new RegExp('(' + tag[this.props.tagsProperty] + ')', 'gi');
for(let i = 0; i < text.length; i++){
if(text[i].match(regex)){
/**
* Pretty simple if its a one-word tag, search for the word and replace.
* could potentially cause some mis-matched tags but the words
* in my usecase are pretty specific, unlikely to be used in
* normal dialogue.
*/
text[i] = <TriggerModal data={tag} label={tag[this.props.tagsLabelProperty || 'name']} />;
}else{
// for tags with spaces, split them up.
let tempTag = tag[this.props.tagsProperty].split(' ');
// check for length
if(tempTag.length > 1) {
// we will be replacing at least 1 item in the array
let replaceCount = 0,
startIndex = null;
// If the first word of tempTag matches the current index, loop through the rest of the tempTag and check to see if the next words in the text array match
if(tempTag[0].toLowerCase() === text[i].toLowerCase()){
startIndex = i;
replaceCount += 1;
// loop through temp array
for (let j = 0; j < tempTag.length; j++) {
if(tempTag[j].toLowerCase() === text[i+j].toLowerCase()){
replaceCount += 1;
}
}
// Push data into replaceIndexes array to process later to prevent errors with adjusting the indexes of the text object while looping
replaceIndexes.push({
startIndex: startIndex,
replaceCount: replaceCount,
element: <TriggerModal data={tag} label={tag[this.props.tagsLabelProperty || 'name']} />
});
}
}
}
}
});
}
// Loop through each replace index object
replaceIndexes.forEach((rep, index) => {
text.splice(rep.startIndex - index, rep.replaceCount, [rep.element, ', ']);
});
// Since we stripped out spaces, we need to put them back in the places that need them.
return text.map(item => {
if(typeof item === "string"){
return item + ' ';
}
return item;
});
}
Edit: This is actually pretty buggy. I ended up ditching my own solution in favor of this package

loop through array to find matches and return every possible solution

I have a small input field where this code gets activated everytime a key is pressed inside it. But it now only prints "found something" when the name exacly matches what you type in the input field.
How can change a part that when I type something like "b" it already removes the matches where there is no "b" in the name is and print every possible matches that still have a "b".
My small code to find the match.
Info is my json big array where I can loop through all the names with info[i].name
var textInput = $findperson.find('input').val();
console.log(textInput);
for (i = 1; i < info.length; i++) {
if (textInput === info[i].name) {
console.log('found something');
}
}
Set Flag if found any match and print them, otherwise print found nothing,
for gi g mean search globally and i mean ignore case sothat A will match a and vise verse.
var textInput = $findperson.find('input').val();
console.log(textInput);
found = false
for (i = 1; i < info.length; i++) {
if (info[i].name.match(new RegExp(textInput,"gi")) ) {
console.log(info[i].name);
found = true
}
}
if(!found){
console.log("found nothing")
}
I would use regex like this:
var textInput = $findperson.find('input').val();
var regex = new Regexp(".*("+textInput+").*","i");
var filtered = info.filter(function (current) {
return current.name.match(regex);
});
console.log(filtered);
Just use indexOf to search for one String within another:
if(info[i].name.indexOf(textInput) != -1) {
indexOf will return -1 if String isn't found within the other.
You can try searching for some letters in one of the results 'balloon', 'ball', 'apple' in the example below:
var results = ['balloon', 'ball', 'apple'];
function filterResults() {
var input = document.getElementById('input').value;
var resultsFiltered = results.filter(function(a) {
return a.indexOf(input) != -1;
});
var result = ''; resultsFiltered.map(function(a) {
result += a + '<br/>';
}); document.getElementById('result').innerHTML = result;
}
<input id='input' onkeyup='filterResults();'/>
<div id='result'></div>

Categories

Resources