I am creating a work tool for notepad abbreviations. As the company I work for is strict about downloading any external tools I've resorted to using Javascript and HTML built on notepad.
I've been able to replace single words such as when I type "Vacancy" it returns "VAC". Or when typing "Payment" it returns "PYMT". My issue is trying to replace multiple words into 1 small abbreviation. For instance "Follow Up" I want to return "F/U". With the spaces I'm finding it is not working.
Tried multiple ways but unable to figure this out.
Here is the code snippet that I've used
function myFunction() {
var str = document.getElementById("demo").value;
var mapObj = {
Payment:"PYMT",
Vacancy:"VAC",
str = str.replace(/Payment|Vacancy, fucntion(matched){
return mapObj[matched];
});
alert(str);
document.getElementById("demo").value = res;
}
What I would like to do is add my mabObj
so it would read
function myFunction() {
var str = document.getElementById("demo").value;
var mapObj = {
Follow Up:"F/U"
str = str.replace(/Follow Up|, fucntion(matched){
return mapObj[matched];
});
alert(str);
document.getElementById("demo").value = res;
}
JavaScript objects can have properties with spaces in them, but in order to do so, the property name needs to have quotes around it.
That said, I would suggest using a Map in this case, as it will allow you to match any string without worrying about naming collisions with properties from the object's prototype.
const abbreviation = new Map([
['Follow Up', 'F/U'],
['Payment', 'PYMT'],
['Vacancy', 'VAC']
]);
const input = 'Payment noise Vacancy noise Follow Up noise Vacancy';
const pattern = new RegExp(Array.from(abbreviation.keys()).join('|'),'g');
const result = input.replace(pattern, (matched) => {
return abbreviation.get(matched) || matched;
});
console.log(result); // 'PYMT noise VAC noise F/U noise VAC'
To include a key with a space in an object you can put it in brackets like {["Follow Up"]: "F/U"}
function replaceKeyWords(str) {
var mapObj = {
Payment:"PYMT",
Vacancy:"VAC",
["Follow Up"]:"F/U",
};
str = str.replace(/(Payment|Vacancy|Follow Up)/, function(matched){
return mapObj[matched];
});
return str;
}
console.log(replaceKeyWords("Payment"));
console.log(replaceKeyWords("Vacancy"));
console.log(replaceKeyWords("Follow Up"));
Related
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.
Let's say for some delicate reason I have the following react state:
{ superGreeting: 'Hello!!!' }
Now, assume I have this complicated operation that basically takes the superGreeting string and works on it, in the end replacing a character at a specific position. New state then should be:
{ superGreeting: 'Hullo!!!' }
So, there would be my action:
action = (index) => {
var { superGreeting: newGreeting } = this.state;
newGreeting[index] = 'u';
this.setState({superGreeting: newGreeting});
}
Unfortunatelly, such approach does not work and ends with:
TypeError: Cannot assign to read only property '1' of string 'Hello!!!', indicating this line as the offending one: newGreeting[index] = 'u'
I use react.js, ES6, no redux, no mobx, no immutable.js. Thought that the issue is caused by the string still being related/used by the soon-to-be-previous state, so I thought that creating a copy would work (I tried newGreeting = newGreeting.toString(), '' + newGreeting, `${newGreeting}`, ''.concat(newGreeting), without any success). Any ideas?
Strings in JavaScript are immutable. Your example can be trimmed down to
(function(){
"use strict";
var str = 'Hullo!!!';
str[1] = 'e';
})();
If you want to mutate a string, you'll need to create a new string, e.g.
(function(){
"use strict";
var str = 'Hullo!!!';
str = str.slice(0, 1) + 'e' + str.slice(2);
})();
Strings in JS are immutable, but you can turn it into an array, work on it, then join it back together.. also there are string functions like substr and replace which return a new string if those are applicable.
var split = superGreeting.split('')
split[index] = 'u'
var newGreeting = split.join('')
Your problem here does not have anything to do with react. Strings in javascript are immutable.
You could create the following helper function:
var replaceCharAt = function(str, index, c) {
return str.slice(0, index) + c + str.slice(index+1)
}
so that
replaceCharAt('012', 1, 'x') === '0x2'
Hi I want to write clean code that I can read and have a good overview.
So I wrote this:
var id = '12345';
var coll = ['scc-roles','scc-proj-' + id];
var spm = 'some-role';
var data = {role : spm, roleNames : 'sccss-user', collection : coll}
var spmRoleId = xdmp.eval('declareUpdate();
var sec = require("/MarkLogic/security.xqy");
var roleId = sec.createRole(role, "Generated project member", roleNames, null, collection,null,null);
var uri = "http://marklogic.com/xdmp/roles/" + roleId;
xdmp.documentAddCollections(uri,collection)',data,{"database" : xdmp.securityDatabase()})
But apparently a newline is not allowed in xdmp.eval() ?
[javascript] JS-JAVASCRIPT: + 'var sec = require("/MarkLogic/security.xqy"); -- Error running JavaScript request: SyntaxError: Unexpected token ILLEGAL
I tried using a '+' sign to generate a strng over more then one line, swapping single and double quotes but no luck.
Being able to test this code (copy paste) to the security database makes a lot of sense to me...
If I wrap it all in one unreadable line , it works ok.
hugo
The way to effectively create a new line in a JavaScrit string is to escape the new line char like this
var str = "I'm displayed\
in two line";
In the final file, you will see effectively a new line.
If you want see in the dist output the new line but not in your src string you could just insert the \n equivalent of a return to line.
var str = "I'm displayed\n in two line";
In es6 you will be able to use ` char to achieve the same thing without \
var str = `I'm displayed
in two line`;
Maybe you would like the strange, yet useful array-notation way of doing this:
var multiline1 = [
'the lazy fox',
'jumped over',
'the dead chicken',
].join('\n');
and the result:
the lazy fox
jumped over
the dead chicken
In general, you should avoid string concatenation to build code for eval. Strings make it difficult to spot bugs and are a great vector for injection attacks. Instead, I'd advise you to write a proper function in XQuery or JavaScript and use xdmp.invokeFunction to evaluate it. invokeFunction takes all of the same options as xdmp.eval.
Here's an example that gets roles in the context of a security database. The applyAs function returns a function that wraps the function provided by the caller, evaluating it with the eval options provided.
function applyAs(fct, options) {
return function() {
var params = Array.prototype.slice.call(arguments);
// Curry the function to include the params by closure.
// xdmp.invokeFunction requires that invoked functions have
// an arity of zero.
var f = (function() {
return fct.apply(null, params);
}).bind(this);
// Allow passing in user name, rather than id
if(options.user) { options.userId = xdmp.user(options.user); delete options.user; }
// Allow the functions themselves to declare their transaction mode
if(fct.transactionMode && !(options.transactionMode)) { options.transactionMode = fct.transactionMode; }
return xdmp.invokeFunction(f, options); // xdmp.invokeFunction returns a ValueIterator
}
}
/**
* Gets an Array of id-name Objects. Requires privileged access to security.
*
* #param names An optional Array of role IDs as strings used to filter
* #return An Array of Objects with role ID keys and role name values
*/
function getRoles(names) {
var sec = require('/MarkLogic/security.xqy');
var db = {database: xdmp.securityDatabase()};
var roleIDs = applyAs(sec.getRoleIds, db);
var rolesItr;
if(Array.isArray(names)) {
rolesItr = roleIDs(xdmp.arrayValues(names));
} else {
rolesItr = roleIDs();
}
var roleNames = applyAs(sec.getRoleNames, db)(rolesItr).toArray().map(function(el) { return el.textContent; });
var roles = [];
var i = 0;
for(var role of rolesItr) {
var r = {}
r[role.textContent] = roleNames[i++];
roles.push(r);
}
return roles;
}
getRoles();
Originally from a gist.
I wish to do a search and replace in a string. It searches for any word that begins with "$" and replaces it with a value from an array. For example if the string is:
[div class='news'][h4]$title[/h4][p]$desc[/p][/div]
It replaces [] to <> (already done). But then i want it to replace $title with data from an array. So data["title"] and then $desc would be replaced with data["desc"].
The code i have so far is
var obj = $('#'+id);
var url = $(obj).attr('loadJSON');
var format = $(obj).attr('responseFormat');
$.getJSON(url, function(data) {
var html = "";
for(var i=0;i<data.length;i++) {
var tmp = format;
tmp = tmp.replace(/\[+(.*?)\]+/g,"<$1>");
tmp = tmp.replace();
}
});
The format is the string which it will replace in, and data (from the JSON response) is the array which i want the variables to change to.
Could someone please help me with this? Thanks in advance
then add as last replacement
tmp = tmp.replace(/\$([a-z]+)/gi, function(match, v) {
return data[v] || v;
})
note that in case of data[v] is undefined you could return something else like data[v] || ["not found", v].join(' ') just to track what variable is missing
I'm no JS expert, but this looks wrong in so many ways... your data object should be a JS object, that is something like { title: 'Generic title.', description: 'It's a generic title' }.
Why not put the values into paragraph elements, and then insert those into the div (i.e., you append them). Something like this:
$.getJSON(url, function(data) {
$('div.news').append($('<p />').text(data.title));
$('div.news').append($('<p />').txt(data.description));
});
You could do
tmp = tmp.replace(/\$([a-z]+)/gi, function(match) {
match = match.replace('$', '');
return (data[match] || match)
});
http://jsfiddle.net/uZbk8/
What is the best method for splitting or extracting the css properties out of as string and into an object?
var cssProperties = 'background:green;content:"Content;";color:pink;';
The above should result in the following
var theObject = {
background:'green',
content:'"Content;"',
color:'pink'
}
Unfortunately I can not just use a split(";") and cycle through the array due to the semicolon in the url. I could create a giant loop that cycles through every character while skipping the ";" only while wrapped in quotes, but that seems kinda of wrong.
Is there a regex trick for this?
Optional:
Also are there any really good regex websites. I understand most of the syntax but there doesn't seem to be many practical really complicated examples on most of the websites I have found.
Here is a fiddle further demonstrating the function: http://jsfiddle.net/ZcEUL/
(function() {
var div = document.createElement('div'),
rprops =/[\w-]+(?=:)/g,
rcamelCase = /-(\D)/g,
fcamelCase = function(a,letter) {
return letter.toUpperCase();
};
window['styleToObject'] = function(str) {
var props = str.match(rprops),
prop, i = 0,
theObject = {};
div.style.cssText = str;
while (prop = props[i++]) {
var style=div.style[prop.replace(rcamelCase,fcamelCase)];
if (style) {
theObject[prop] = style;
}
}
return theObject;
};
})();
Here was the solution I made regarding your first css string you had listed... Not the best but maybe it'll help spark some ideas.
JSFiddle Example
Try this or something similar
var newString = cssProperties
.replace(":", ":'")
.replace(";", ", '");
var obj = eval(newString);