array every() with includes() issues - javascript

Hey people so i've spent a bit too much time trying to find a solution for this and I'd like to know if anyone can help me.
So the problem is, I have 2 arrays, first one with multiple words from an address and another one I built with word I entered in an input, it is returning true if I put the exact same word in the input as it is in the string array but I would like it to return true if it partially match too, for example I have "CENTER" and "RACHEL" in the array of string if I write "CEN EL in the input because it would partially match 2 of the array elements
here's the code i've been trying to put together
function checker(arr, words) {
return words.every(v => arr.includes(v));
}
this.shippingAddress.forEach(address => {
const stringBuild = `${address.name} ${address.display_address} ${address.phone}`;
const arrayString = stringBuild.split(' ');
if (checker(arrayString, words)) {
_results.push(address);
}
});
An example of input would be for the array arrayString:
[
0: "CENTER"
1: "MANIKI"
2: "BRUCHESI"
3: "2225,"
4: ""
5: "STREET"
6: "RACHEL"
7: "EAST,"
8: "CITY"
9: "STUFF,"
10: "STUFF"
11: "STUFF"
12: "STUFF"
13: "STUFF"
]
for the array words:
[
0: "CEN"
1: "EL"
]
and the output would be true because CEN and EL passing in the checker would be included in the first array but it only work if I put the full words in

If I understand correctly, you want to return true if every word in the array is partially (or totally) matched?
For every word that must be matched, I would test the array of typed words, and see if at least one of the typed words partially matches the target. I'd use a regex, and also make it case-insensitive with "i" :
const checkArray = ["center","rachel"];
const isOK = (typedWords) => checkArray.every( word1 => typedWords.some( word2 => new RegExp(word2, "i").test(word1)))
console.log(isOK([ "ent" , "rac" ]))
console.log(isOK([ "AaAa" , "BbBb" ]))

Related

Comparing two RegEx objects in Node.js

I'm using NodeRED to perform some logic on a string which has been created from image analysis (OCR) on Microsoft Azure Cognitive Services. The image analysis doesn't allow for any pattern matching / input pattern.
The resulting string (let's call it 'A') sometimes interprets characters slightly incorrectly, typical things like 'l' = '1' or 's' = '5'.
The resulting string can be one of only a few different formats, for argument sake lets say:
[a-z]{4,5}
[a-g]{3}[0-9]{1,2}
[0-9][a-z]{4}
What I need to do is determine which format the intepretted string ('A') most closely aligns to ('1', '2' or '3'). Once I establish this, I was planning to adjust the misinterpretted characters and hopefully be left with a string that is (near) perfect.
My initial plan was to convert 'A' into RegEx - so if 'A' came back as "12345", I would change this to a RegEx object [1|l][2|z]34[5|s], compare this object to the RegEx objects and hopefully one would come back as a match.
In reality, the interpretted string is more like 8 alphanumeric and five different (fairly complex) RegEx possibilities, but I've tried to simplify the problem for the purposes of this question.
So the question: is it possible to compare RegEx in this way? Does anyone have any other suggestions on how this image analysis could be improved?
Thanks
Here is a solution using a Cartesian product to compare a string for possible matches. Test string is 'abclz', which could match pattern1 or pattern2:
const cartesian = (...a) => a.reduce((a, b) => a.flatMap(d => b.map(e => [d, e].flat())));
const charMapping = {
'1': ['1','l'],
'l': ['1','l'],
'2': ['2','z'],
'z': ['2','z'],
'5': ['5','s'],
's': ['5','s']
};
const buckets = {
pattern1: /^[a-z]{4,5}$/,
pattern2: /^[a-g]{3}[0-9]{1,2}$/,
pattern3: /^[0-9][a-z]{4}$/
};
const input = 'abclz';
console.log('input:', input);
let params = input.split('').map(c => charMapping[c] || [c]);
let toCompare = cartesian(...params).map(arr => arr.join(''));
console.log('toCompare:', toCompare);
let potentialMatches = toCompare.flatMap(str => {
return Object.keys(buckets).map(pattern => {
let match = buckets[pattern].test(str);
console.log(str, pattern + ':', match);
return match ? str : null;
}).filter(Boolean);
});
console.log('potentialMatches:', potentialMatches);
Output:
input: abclz
toCompare: [
"abc12",
"abc1z",
"abcl2",
"abclz"
]
abc12 pattern1: false
abc12 pattern2: true
abc12 pattern3: false
abc1z pattern1: false
abc1z pattern2: false
abc1z pattern3: false
abcl2 pattern1: false
abcl2 pattern2: false
abcl2 pattern3: false
abclz pattern1: true
abclz pattern2: false
abclz pattern3: false
potentialMatches: [
"abc12",
"abclz"
]

How to replace list of words with <span> tag at multiple indexes in Javascript

I have a response of parent string which I have to modify and replace with the provided start and end indexes.
let parentString = '\r\nManchester United won\r\nManchester City lost\r\nLeeds United tied'
let replaceValues =
{value: 'Manchester United', startIndex: 2, endIndex: 19}
{value: 'Manchester City', startIndex: 25, endIndex: 40}
{value: 'Leeds United', startIndex: 47, endIndex: 59}
Expected Final Result:
I tried below approach but was not successful
replaceAt(input: string, index: number, replacement: string, source: string) {
return (
input.substring(0, index) +
replacement +
input.substring(index + source.length)
);
}
Usage:
replaceValues.forEach((replaceMatch: any) => {
parentString = this.replaceAt(
parentString,
replaceMatch.startIndex,
"<span class='replace-text-{{i}}'>${replaceMatch.value}</span>",
replaceMatch.value
);
please ignore my example names couldn't think anything more
EDIT: My previous answer did not account to duplicate and did not use your indexes, so here it is a more consistent answer:
Convert string to array to ease manipulation
const parentArray = Array.from(parentString)
Now we have an array of characters, i.e [" ", " ", "M", "a", "n", "c", "h", ...]
For each item in replaceValues we use splice on our newly created array. Splice acctepts 3 arguments:
First argument is the start index where we want to splice the array.
Second argument is how many items in the array will be deleted/replaced.
Third argument is with what we want to replace the array portion.
let numberOfCharsReplaced = 0
replaceValues.forEach(item => {
parentArray.splice(item.startIndex - numberOfCharsReplaced, item.endIndex - item.startIndex, `<span>${item.value}</span>`)
numberOfCharsReplaced = numberOfCharsReplaced + item.endIndex - item.startIndex - 1
console.log(parentArray, numberOfCharsReplaced)
})
That numberOfCharsReplaced is necessary because since we splice and replace, we need to take account of the number of chars that has been replaced, what I am saying is that when we replace the 'Manchester United' word that has 16 chars, we pass from 16 items in the array to only 1 big word (i.e "<span>Manchester United</span>") so we can't rely on the startIndex of the next value only, we need to do some calculation. It's easier in the code.
We get back our string by using .join(), telling to the join method with which character we want to join each character.
const replacedParentString = parentArray.join("");
If you still wish to have an array of html string, use the split and shift method indicated in the old answer
Please refer to MDN to read more about the methods used in this answer
splice
join
OLD ANSWER
Use values to replace names with their 'html' equivalent within the parent string
replaceValues.forEach(item => {
parentString = parentString.replace(item.value, `<span>${item.value}</span>`)
})
Now you have a string that is like this:
\r\n<span>Manchester United</span> won\r\n<span>Manchester City</span> lost\r\n<span>Leeds United</span> tied
So now you may want this string as an array of html content
let contentsArray = parentString.split("\r\n")
Now we have this:
[
"",
"<span>Manchester United</span> won",
"<span>Manchester City</span> lost",
"<span>Leeds United</span> tied"
]
Finally if you want to get rid of that initial empty string just shift the array once
contentsArray.shift()
If you don't want to use regex you can try this code :
let parentString = '\r\nManchester United won\r\nManchester City lost\r\nLeeds United tied'
let replaceValues = [
{value: 'Manchester United', startIndex: 2, endIndex: 19},
{value: 'Manchester City', startIndex: 25, endIndex: 40},
{value: 'Leeds United', startIndex: 47, endIndex: 59},
];
replaceValues.sort((a,b) => b.startIndex - a.startIndex);
function replaceAt(input, start, end, value) {
let str = input.split('')
str.splice(start, end - start, value);
return str.join('');
}
for(let replace of replaceValues) {
parentString = replaceAt(parentString,replace.startIndex, replace.endIndex, `<span class='replace-text-{{i}}'>${replace.value}</span>`);
}
console.log(parentString);
// Output :
// <span class='replace-text-{{i}}'>Manchester United</span> won
// <span class='replace-text-{{i}}'>Manchester City</span> lost
// <span class='replace-text-{{i}}'>Leeds United</span> tied
I don't know where does {{i}} comes from, but I think you can easily fill it will the correct value
Maybe regex is slightly faster? Seems like you indend to get rid of line breaks?
const parentString = '\r\nManchester United won\r\nManchester City lost\r\nLeeds United tied'
const repalcedString = parentString.replace(/(\r\n|\n|\r)/gm, "");
console.log(repalcedString)

In Node/Javascript, how do I map cardinality from string to int?

For example, I have user input any string: "1st", "2nd", "third", "fourth", "fifth", "9999th", etc. These are just examples, the user can input any string.
I want to map this to integer cardinality:
"1st" -> 0
"2nd" -> 1
"third" -> 2
"fourth" -> 3
"fifth" -> 4
"9999th" -> 9998
So I need some kind of function where:
function mapCardinality(input: string): number{
let numberResult:number = ??
return numberREesult;
}
and I can call it like this:
console.log(
mapCardinality("1st"), // print 0
mapCardinality("2nd"), // print 1
mapCardinality("third"), // print 2
mapCardinality("fourth"), // print 3
mapCardinality("fifth"), // print 4
mapCardinality("9999th") // print 9998
);
Just look it up in an array or parse it as number:
const mapCardinality = c => {
const pos = ["1st", "2nd", "third", "fourth", "fifth"].indexOf(c);
return pos === -1 ? parseInt(c, 10) - 1 : pos;
};
I'd first ask what are the suffixes for all of the inputs?
'nd', 'rd', 'st', 'th' (most numbers)
If they enter an integer with the above prefixes then you could write the following function:
const getInteger = input => input.slice(0, -2);
const num = getInteger('999th');
console.log(num); // prints "999"
If they enter the elongated variant, it becomes much more complex, especially when it comes to typos, lack of spaces, etc. One way could be to map single digit words ('one', 'two', etc), tens ('ten', 'twenty', etc'), hundreds, thousands, and so on instead of every number imaginable. I would then parse and find matching words to give a result. That being said it is still limiting. I would strongly suggest limiting user input formats. Why can't the user input an integer?
const cardinalDictionary = {
'zero': 0,
'one': 1,
...,
'twenty',
...,
'hundred': 100,
'thousand': 1000,
};

Combining values from two arrays

I am working on an Express app and have an issue trying to match up the values of two arrays
I have a user-entered string which which come through to me from a form (e.g.let analyseStory = req.body.storyText). This string contains line breaks as \r\n\.
An example of string is
In the mens reserve race, Cambridges Goldie were beaten by Oxfords
Isis, their seventh consecutive defeat. \r\n\r\nIn the womens reserve
race, Cambridges Blondie defeated Oxfords Osiris
However before I print this to the browser the string is run through a text analysis library called pos e.g.
const tagger = new pos.Tagger();
res.locals.taggedWords = tagger.tag(analyseStory);
This returns to me an array of words in the string and their grammatical type
[ [ 'In', 'Noun, sing. or mass' ],
[ 'the', 'Determiner' ],
[ 'mens', 'Noun, plural' ],
[ 'reserve', 'Noun, sing. or mass' ],
[ 'race', 'Noun, sing. or mass' ],
[ ',', 'Comma' ],
[ 'Cambridges', 'Noun, plural' ],
[ 'Goldie', 'Proper noun, sing.' ],
[ 'were', 'verb, past tense' ],
[ 'beaten', 'verb, past part' ],
[ 'by', 'Preposition' ],
[ 'Oxfords', 'Noun, plural' ],
....
]
Currently when I print this user-entered text to the screen I loop through the array and print out the key and then wrap that in a class containing the value. This gives a result like:
<span class="noun-sing-or-mass">In</span>
<span class="determiner">the</span>
<span class="noun-plural">mens</span>
so that I can style them.
This all works fine but the problem is that I lose my line breaks in the process. I'm really not sure how to solve this problem but I was thinking that perhaps I could do this on the client side if I break the initial string I get (analyseStory) into an array (where commas, full stops are array items as they are in the above) and then apply the grammatical type supplied in res.locals.taggedWords to the array generated from analyseStory string. However I'm not sure how to do this or even if it is the right solution to the problem.
FWIW if I print analyseStory to the screen without pushng it through text analysis I handle line breaks by wrapping the string in <span style="white-space: pre-line">User entered string</span> rather than converting to <br />.
Any help much appreciated.
This solution uses ES6 Map, and String.replace() with a RegExp to find all words in the analysis, and replace them with a span that has the relevant class name.
You can see in the demo that it preserves the line breaks. Inspect the elements to see the spans with the classes.
const str = 'In the mens reserve race, Cambridges Goldie were beaten by Oxfords Isis, their seventh consecutive defeat. \r\n\r\nIn the womens reserve race, Cambridges Blondie defeated Oxfords Osiris';
const analyzed = [["In","Noun, sing. or mass"],["the","Determiner"],["mens","Noun, plural"],["reserve","Noun, sing. or mass"],["race","Noun, sing. or mass"],[",","Comma"],["Cambridges","Noun, plural"],["Goldie","Proper noun, sing."],["were","verb, past tense"],["beaten","verb, past part"],["by","Preposition"],["Oxfords","Noun, plural"]];
// create Map from the analyzed array. Use Array.map() to change all keys to lower case, and prepare the class name
const analyzedMap = new Map(analyzed.map(([k, v]) =>
[k.toLowerCase(), v.trim().toLowerCase().replace(/\W+/g, '-')]));
// search for a sequence word characters or special characters such as comman and period
const result = str.replace(/(:?\w+|,|.)/gi, (m) => {
// get the class name from the Map
const className = analyzedMap.get(m.toLowerCase());
// if there is a class name return the word/character wrapped with a span
if(className) return `<span class="${className}">${m}</span>`;
// return the word
return m;
});
demo.innerHTML = result;
#demo {
white-space: pre-line;
}
<div id="demo"></div>
<span> is not a block level element. By default it will not line break. You need to either make it block level with css or wrap your text in something that is block level like a <p> tag.
CSS To Make Block
span { display: block; }
You can pre-process text before analyzing text and replace line breaks with some special characters. Something like the following:
const story_with_br = analyseStory.replace(/\n/g, "__br__");
const tagger = new pos.Tagger();
res.locals.taggedWords = tagger.tag(story_with_br);
Hopefully, taggedWords array will contain "__br__" and if it does then while rendering you can add line breaks instead of "__br__"
What you can do is :
Option 1
Edit the library you're using so that it doesn't ignore your \r\n
Option 2
Define a complex key which will define the newlines :
const newlinesKey = 'yourkeyvalue';
Then you replace all newlines by your newlinesKey :
analyseStory.replace(/\r\n/g, newlinesKey);
And after that you can call the text analysis library :
const tagger = new pos.Tagger();
res.locals.taggedWords = tagger.tag(analyseStory);
Like this you would be able to detect when you have to put a new line if the tagger doesn't ignore the keyValue.

Javascript Object negative number

Create object adjustementType inside script tag in MVC 5 partial view.
<script type="text/javascript">
var adjustementType = { -1:'Rate Decrease', 1: 'Rate Increase' };
</script>
but getting following error "Expected identifier, string or number" . The error is thrown on area -1 & 1 field.
You cannot use strings with spaces because you are defining variables and you also should turn type around like this:
var adjustementType = { RateDecrease: -1, RateIncrease: 1 };
alert(adjustementType.RateDecrease); //-1
This is because you are actually defining enums.
EDIT: You can use strings with spaces too but then you are dealing with them like arrays. But I think this doesn't make any sense.
var enumtype = { "-1": "Rate Decrease", "1" : "Rate Increase"};
alert(enumtype["1"]); //Rate Increase
alert(enumtype["-1"]); //Rate Decrease
Since -1 is not valid identifier you need to take such keys into quotes when you define an object properties:
var adjustementType = { '-1': 'Rate Decrease', 1: 'Rate Increase' };
then you will be able to access it using bracket notation:
alert(adjustementType[-1]);

Categories

Resources