Remove strings if found remove a part of the string in JS - javascript

I'm facing a problem.
I have this:
<input type="hidden" name="Boss" id="Boss" value="8,116,167,198,139,203,158,170,">
Actually I have this code in js:
// On click on element with class .Boss
$("form").on("click", ".Boss", function(event){
var clickedId = $(this).attr('value')+','; // give me 8,
var locationBtn = $('#Boss'); // Select the input
var locationBtnValue = $('#Boss').val(); // Take the select value
if(locationBtnValue.toString().indexOf(clickedId) == -1) { locationBtn.val(locationBtnValue + clickedId); }
else { locationBtn.val(locationBtnValue.replace(clickedId,'')); }
});
My problem is: if want to decide to remove the 8 my javascript do not remove the item 8, but the first occurrence it will find in my string, so 8,116,167,19**8,**139,203,158,170,. So it breaks my other item...
How can I make to do not break it ?
Thanks.

I do not know what your final outcome is, but I think you want it to be 116,167,198,139,203,158,170, In this case you can split and filter the array to get rid of the value.
var str = "8,116,167,198,139,203,158,170,"; //the input
var updated = str.split(",") //turn it into an array
.filter(function (val) { //loop through all the elements applying this function
return val!=="8"; //keep the item if the index does not match the item
}
).join(","); //turn array back into a string

One way to be consistent is by splitting it into an array and then removing the occurence.
// On click on element with class .Boss
$("form").on("click", ".Boss", function(event) {
var clickedId = $(this).attr('value'); // give me 8
var locationBtn = $('#Boss'); // Select the input
var locationBtnValue = locationBtn.val(); // Take the select value
var ids = locationBtnValue.split(','); //split into an array
var index = ids.indexOf(clickedId); //index of clickedId inside ids
if(index > -1) { //found
ids = ids.splice(index, 1); //remove from ids
} else {
ids.push(clickedId); //add to ids
}
locationBtn.val(ids.join(','));
});

That's what replace does when you pass it a string, it replaces the first occurrence.
You need to pass it a regular expression with the global modifier, like this
locationBtnValue.replace(/8,/g,'')
you can do the same thing with the RegExp constructor, and create a regular expression from the string you have
var clickedId = $(this).val() + ',';
var regex = new RegExp(clickedId, "g");
locationBtnValue.replace(regex,'');

Related

pure javascript, filter box doesn't filter properly

I have an array of devices, each device has own unique id, I wish to make searchBox which will filter by this id. So far I managed it partly, so it checks if character from input match character from device-id. However not in way I would want that, example below:
id =35678; input.value = 5
it shouldn't work as first character is 3
id = 35679; input.value= 35
it should work as first character is same
Problem is in match/include function, but no really idea how to replace it to make it work
input_box.addEventListener('keyup', function(){
var search_result = this.value;
var device = document.querySelectorAll('[device_id]')
var array_of_devices = [];
for (var i = 0; i < device.length; i++) {
array_of_devices.push(device[i].getAttribute('device_id'))
}
array_of_devices.forEach(el => {
if (!el.match(search_result)) {
var not_matched = document.querySelector(`[device_id="${el}"]`)
not_matched.style.display = "none"
} else {
var matched = document.querySelector(`[device_id="${el}"]`)
matched.style.display = "block"
}
})
})
You can use startsWith instead of match
let arr = ['35678', '451234', '45454', '56565']
let find = (value) =>{
return arr.filter(id=> id.startsWith(value))
}
console.log(find(5))
console.log(find(35))
console.log(find(46))
Instead of using .match you can simply use .indexOf and check the index, if it 0 then the entered string is matching the device name from the begining.
array_of_devices.forEach(el => {
// Condition below is changed to use indexOf
if (el.indexOf(search_result) === 0) {
var not_matched = document.querySelector(`[device_id="${el}"]`)
not_matched.style.display = "none"
} else {
var matched = document.querySelector(`[device_id="${el}"]`)
matched.style.display = "block"
}
});
I would suggest you to build a string of device elements based on the search string and add it to your DOM instead of modifying the display properties. This is costing you a bunch of DOM operations which is computationally heavy.
Notice each id in array should be string
const ids = ['3575', '5555']
const found = value => ids.filter(i => i.indexOf(value, 0) === 0)
console.log(found(5));
console.log(found(35));

Compare and remove array elements

i have a bug in this code that i cannot seem to solve. if there is only 1 instance of Act, it works as it should. But when there is more than 1 instance of Act, it breaks. Not sure what I am missing here.
//Find all instances of italics
var findItalics = new RegExp(/(<em>.*?<\/em>)/g);
var italicsArray = [];
var italicCount;
while (italicCount = findItalics.exec(searchInput)) {
italicsArray.push(italicCount[0]);
}
//Find the italics containing the word 'Act'
var keywordItalics = new RegExp(/<em>.*?(Act).*?<\/em>/g);
var keywordItalicArray = [];
var italicCountKeyword;
while (italicCountKeyword = keywordItalics.exec(italicsArray)) {
keywordItalicArray.push(italicCountKeyword[0]);
}
//Remove all instances of the keyword(s)
for (var tlcs = italicsArray.length - 1; tlcs >= 0; tlcs--) {
if(italicsArray[tlcs] == keywordItalicArray) {
italicsArray.splice(tlcs, 1);
}
}
Thanks to #artgb who helped me rethink this.
//Find all instances of italics
var findItalics = new RegExp(/(<em>.*?<\/em>)/g);
var italicsArray = [];
var italicCount;
while (italicCount = findItalics.exec(searchInput)) {
italicsArray.push(italicCount[0]);
}
//Find the italics containing the word 'Act'
var keywordItalics = new RegExp(/<em>.*?(Act).*?<\/em>/g);
var keywordItalicArray = [];
var italicCountKeyword;
while (italicCountKeyword = keywordItalics.exec(searchInput)) {
keywordItalicArray.push(italicCountKeyword[0]);
}
//Remove all instances of the keyword(s)
for(var xXx = 0; xXx < keywordItalicArray.length; xXx++){
for (var tlcs = italicsArray.length - 1; tlcs >= 0; tlcs--) {
if(italicsArray[tlcs] == keywordItalicArray[xXx]) {
italicsArray.splice(tlcs, 1);
}
}
}
var keywordItalics = new RegExp(/<em>.*?(Act).*?<\/em>/g);
Should usually be shortened to:
var keywordItalics = /<em>.*?(Act).*?<\/em>/g;
Where your () are, this would only get a capture of "Act", so to capture whole string in the em, it should be:
var keywordItalics = /<em>(.*?Act.*?)<\/em>/g;
However, a faster way (without regexp) you could get an array of all the emphasized tags just by:
var keywordItalics = document.getElementsByTagName('em');
If you're just trying to get rid of all em's containing "Act", all you should need is:
document.body.innerHTML = document.body.innerHTML.replace(
/<em>.*?Act.*?<\/em>/g,
''
);
This should remove all traces of em's containing "Act" in the document (effectively replacing those strings with an empty string, aka nothing). It will cause a reflow, however. If they are inside a containing element besides body, would be better to get containing element first (instead of using body). There are "better" ways of doing this, but this is probably simplest coding-wise.
Update: an easy way to remove em's with "Act" from the array would be:
italicsArray = italicsArray
.join('_SEP_') // Convert to string
.replace(/<em>.*?Act.*?<\/em>/g,'') // Delete matched entries
.replace(/(_SEP_)+/g,'_SEP_') // Collapse multiple seperators
.split('_SEP_') // Convert back to array
;
This basically uses a seperator _SEP_ (to avoid collisions with strings containing ',') and turns the array into a string, deletes all matches to your regexp, removes what would become undefined entries, and recreates the array in the same name.

Search for multiple elements in an array

I want to retrieve inside an array all the elements who match multiple strings (all of them & not necessary words): like a search engine returning all results matching term_searched#1 && term_searched#2.
It's not a question about duplicates in the array (there's none), but about searching for a conjunction of elements: traditionally, the search is for one element, by himself or in disjunction with others (a|b|c). Just want to search (a && b && c).
I tried:
indexOf() : I can work only with one element to locate in the array.
match() : there is no AND operator in a regex expression (only | - sadly, it would be so simple). So I tried to inject these regex expressions
/(?=element1).*(?=element2)/gim
/(?=element1)(?=element2)/gim see here
The first regex expression works, but not at every time: seems very fragile...
So I don't know if I'm in the good direction (match) or if I can't figure what is the right regex expression... Need your advices.
// filter grid by searching on 'input' event
'input #search': (e)=> {
var keypressed = e.currentTarget.value;
// create array on 'space' input
var keyarr = keypressed.toLowerCase().split(" ");
// format each array's element into regex expression
var keyarrReg = [];
for(i = 0; i < keyarr.length; i++) {
var reg = '(?=' + keyarr[i] + ')';
keyarrReg.push(reg);
}
// array to regex string into '/(?=element1).*(?=element2)/gim' format
var searching = new RegExp(keyarrReg.join(".*"), 'mgi');
// set grid
var grid = new Muuri('#gridre', {
layout: {
fillGaps: true,
}
});
if (keypressed) {
// filter all grid's items (grid of items is an array)
grid.filter(function (item) {
var searchoperator = item.getElement().textContent.toLowerCase().match(searching);
// get items + only their text + lower case their text + return true (not false) in the value ('keypressed') is found in them
//var searchoperator = item.getElement().textContent.toLowerCase().indexOf(keypressed.toLowerCase()) != -1;
return searchoperator;
}
[....]
}
}
Edit with Gawil's answer adapted to my initial code (to help if needed)
// filter grid by searching on 'input' event
'input #search': (e)=> {
var keypressed = e.currentTarget.value;
// create array on 'space' input
var keyarr = keypressed.toLowerCase().split(" ");
// convert the array to a regex string, in a '^(?=.*word1)(?=.*word2).*$' format
// here is Gawil's answer, formatted by Teemu
var searching = new RegExp('^(?=.*' + keyarr.join(')(?=.*') + ').*$', 'm');
// set grid
var grid = new Muuri('#gridre', {
layout: {
fillGaps: true,
}
});
if (keypressed) {
// filter all grid's items (grid of items is an array)
grid.filter(function (item) {
// get items + only their text + lower case their text + delete space between paragraphs
var searchraw = item.getElement().textContent.toLowerCase().replace(/\r\n|\n|\r/gm,' ');
var searchoperator = searchraw.match(searching);
return searchoperator;
}
[....]
}
}
The code bellow will log each element of the array containing words cats and dogs.
It uses the regex ^(?=.*word1)(?=.*word2).*$To handle new lines, use this one instead :
^(?=(?:.|\n)*word1)(?=(?:.|\n)*word2).*$
You can add as many words as you want following the same logic, and it does not take order of the words in count.
It is very similar to what you tried, except that you have to do all (?=) checks before matching the string. Indeed, your first regex works only when the words are in the right order (element1 and then element2). Your second regex almost works, but you wrote only lookaheads, so it checks the presence of each word, but won't match anything.
var words = ["cats", "dog"]
var array = [
"this is a string",
"a string with the word cats",
"a string with the word dogs",
"a string with both words cats and dogs",
"cats rule everything",
"dogs rule cats",
"this line is for dog\nbut cats prefer this one"
]
var regexString = "^";
words.forEach(function(word) { regexString += ("(?=(?:.|\n)*"+word+")"); });
var regex = new RegExp(regexString);
array.forEach(function(str) { // Loop through the array
if(str.match(regex)) {
console.log(str); // Display if words have been found
}
});
If I've correctly understood your question, you've an array of strings, and some keywords, which have to be found from every index in the array to be accepted in the search results.
You can use a "whitelist", i.e. a regExp where the keywords are separated with |. Then iterate through the array, and on every member create an array of matches against the whitelist. Remove the duplicates from the matches array, and check, that all the keywords are in the list simply by comparing the length of the matches array to the count of the keywords. Like so:
function searchAll (arr, keywords) {
var txt = keywords.split(' '),
len = txt.length,
regex = new RegExp(txt.join('|'), 'gi'), // A pipe separated whitelist
hits; // The final results to return, an array containing the contents of the matched members
// Create an array of the rows matching all the keywords
hits = arr.filter(function (row) {
var res = row.match(regex), // An array of matched keywords
final, temp;
if (!res) {return false;}
// Remove the dups from the matches array
temp = {}; // Temporary store for the found keywords
final = res.filter(function (match) {
if (!temp[match]) {
// Add the found keyword to store, and accept the keyword to the final array
return temp[match] = true;
}
return false;
});
// Return matches count compared to keywords count to make sure all the keywords were found
return final.length === len;
});
return hits;
}
var txt = "Some text including a couple of numbers like 8 and 9. More text to retrieve, also containing some numbers 7, 8, 8, 8 and 9",
arr = txt.split('.'),
searchBut = document.getElementById('search');
searchBut.addEventListener('change', function (e) {
var hits = searchAll(arr, e.target.value);
console.log(hits);
});
<input id="search">
The advantage of the whitelist is, that you don't have to know the exact order of the keywords in the text, and the text can contain any characters.

Remove an element from array with user input

I'm having an issue with removing items from a Javascript array. I am using a prompt, and when the user inputs a worker's ID number, I want that element to be removed from the array.
At the moment, my code will only remove the final element in the list.
This is my code:
var remove = function(){
var removeID = prompt("Enter ID of the worker you wish to remove: ");
var index = array.indexOf(removeID);
if(removeID == id){
array.splice(index, 1);
showArray();
}
else{
alert("id is not in the system");
}
}
You're mixing up array indices with elements. It should go like this:
var remove = function(){
var removeID = prompt("Enter ID of the worker you wish to remove: ");
var index = array.indexOf(removeID);
if(index > -1){ // HERE!!!
array.splice(index, 1);
showArray();
}
else{
alert("id is not in the system");
}
}
Two additional observations:
If IDs are numbers, then your call to array.indexOf() will always return -1. prompt() will always give you a string, not an integer; you should use parseInt() instead like this:
var removeID = parseInt(prompt("Enter ID of the worker you wish to remove: "));
Since IDs are meant to be unique, and depending on the context, it might make sense to store them inside an object rather than an array so that you don't risk having duplicate IDs.
I think you messed up your logic
if(removeID == id){
should probably be checking to see if the id exists in the array before removing it.
if(index !== -1){
and another guess since you did not give a runnable example:
var removeID = Number(prompt("Enter ID of the worker you wish to remove: "));
http://jsfiddle.net/dk4kb417/1/
This method should remove the index you want. It will ask for the element value. The first index of that value will be deleted:
var array = ['one','two','three',4,5,6,7,8,9,10];
var remove = function(removeID){
var index = array.indexOf(removeID);
if (index>-1) {
array.splice(index, 1);
}
}
remove(prompt("Enter ID of the worker you wish to remove: "));
console.log(array);

Not reporting true when I think it should, instead reporting all false

http://jsfiddle.net/nicktheandroid/QhL2M/
(type "bye" into the input box")
Purpose of the script: Each li contains tag words in it. When the user types one of these tag words in the input box, it will hide all of the li's that do not contain that tag word. Multiple words may be typed into the input, if more than 1 li's tag words matches any of the words in the input, then they are shown. So it's basically a filtered list that allows for multiple words, and shows all matching li's that contain any of the words typed into the input. It only matches whole words, not partial words, and it should only update the list of matches when another whole word has been matched(when multiple words). This is the goal.
My problem is that it's incorrectly reporting that there's a match in this piece of code:
if (matches == true) {
alert('matched true') //incorrectly reports
} else {
alert('matched false') //incorrectly reports
}
while this piece of code is reporting correctly:
if (regex.test($(this).text()) === true) {
matches = true;
// alert('tis true') //correctly reports
} else {
matches = false;
// alert('tis false') //correctly reports
}
Why is it not reporting correctly? (correctly, as in: what I want it to report)
If anyone is good enough at Javascript to see what i'm trying to do and knows how to help me, that'd be fantastic. I'm trying to work my way through this because there's just a few things left to change.
I think the problem is that you are looping through all of the list items with:
$("#list li").each(function() {
and then within each iteration you loop through the list of words with:
$.each(inputWords, function(i, a_filter) {
and then in the inner loop you again process all li elements with:
var containing = $('#list li').filter(function () {
So then in the inner part you're getting confused about just what you're dealing with. I think what you need to do is loop through each element, testing if it has any matches to the input word list, and then hide or show that element as appropriate. Something like this:
var inputWords = inputValue.toLowerCase().split(/[\s,]+/);
$("#list li").each(function() {
var matches = false,
$currentElement = $(this);
$.each(inputWords, function(i, a_filter) {
if ($.trim(a_filter)==="") return; // skip blank items
var regex = new RegExp('\\b(' + a_filter + ')(s|es|\'s)?\\b', 'i');
if (regex.test($currentElement.text())) {
matches = true;
return false; // return false breaks out of the $.each
// (no need to continue once a match is found)
}
});
if (matches)
$currentElement.slideDown(400);
else
$currentElement.slideUp();
});
Working demo: http://jsfiddle.net/QhL2M/19/
EDIT: also the way you were splitting the input word list didn't work for me:
var inputWords = $('.filterinput').text().toLowerCase().split(/[\s,]+/);
// I've changed that to
var inputWords = inputValue.toLowerCase().split(/[\s,]+/);
You don't want to use .text() on an input, and in any case inputValue was already initialised earlier to hold the text of that input. My code above and demo have been updated.
Let me know if this is what you try to accomplish (jsFiddle example):
(function($) {
function fiKeyup() {
var inputValue = $(this).val();
if (inputValue.length > 2) {
var inputWords = inputValue.split(/[\s,]+/ig);
var containing = $('#list li').filter(function () {
var ret = false;
var text = $(this).text().toLowerCase();
$.each(inputWords, function(i, a_filter) {
var regex = new RegExp('\\b(' + a_filter + ')(s|es|\'s)?\\b');
if(regex.test(text)) {
return !(ret = true);
}
});
return ret;
});
$('#list li').not(containing).slideUp();
} else {
$('#list li').slideDown();
}
return false;
};
var timeout;
$('.filterInput').keyup(function() {
var that = this;
clearTimeout(timeout);
timeout = setTimeout(function() {
fiKeyup.call(that);
}, 500);
});
}(jQuery));

Categories