Avoid errors when comparing title field to an array - javascript

The following code gets the first word from my page title, compares it to the values of an array and outputs me a final value :
var fulltitle = "Deep Blue Shoes";
var arr = fulltitle.split(' ').slice(0, 1);
var title = arr && arr.length ? arr[0] : "";
//test variables
testarray = ["blue", "top", "110", "in stock", "deep blue", "down", "111", "in stock"]
//function
function testfunction(array, variable) {
var varindex = array.indexOf(variable.toLowerCase())
return array[varindex + 2]
}
//calling the function
var finalvalue = testfunction(testarray, title);
console.log( finalvalue )
In this case, if my title is Deep Blue shoes, the system cuts the title too early and try to compare the value 'deep' with the values into the array. But the value deep doesn't exist.
I'm trying to find a solution for this and similiar problems that may occur, since my variables can be like 'blue', 'deep blue', 'deep blue sky'. We work with exact matches only.
How would you fix this ?
See also https://jsfiddle.net/Francesco82/hg10a3wy/

You could use a String (or RegEx) comparison?
But in the first example this matches with both "blue" as well as "deep blue" (and in the second "acquacalda" as well as "acqua").
What logic should we use in those cases? If it matches with more than one, choose the one with the most words if one has more words than the others (i.e., in that case, choose "deep blue") and if the matches are the same number of words choose the longer of the two words? i.e. In the second case choose "acquacalda" over "acqua"? (in general, always choosing the more specific answer)
See below and https://jsfiddle.net/alexander_L/gx83mrtL/3/
findPage("Deep Blue Shoes");
findPage("Acquacalda");
function findPage(fulltitle){
//test variables
const testarray = ["blue", "top", "110", "in stock", "deep blue", "down", "111", "in stock", "acqua", "top", "112", "in stock", "acquacalda", "down", "113", "in stock"]
const altMatches = testarray.reduce((aggArr, item) => {
if (fulltitle.toLowerCase().includes(item)){
console.log('we have a match with ' + item);
aggArr.push(item);
}
return aggArr;
}, []);
//then we can choose the "largest" match:
const finalMatch = altMatches.reduce((aggMatch, item) => {
if (aggMatch == null || (aggMatch.split(' ').length < item.split(' ').length) || (aggMatch.length < item.length)){
return item;
}
return aggMatch;
}, null);
console.log('the final match is ' + finalMatch);
const finalPage = testarray.indexOf(finalMatch) + 2;
console.log('the final match page number is ' + testarray[finalPage]);
}
OUTPUT:
"we have a match with blue"
"we have a match with deep blue"
"the final match is deep blue"
"the final match page number is 111"
"we have a match with acqua"
"we have a match with acquacalda"
"the final match is acquacalda"
"the final match page number is 113"

I was able to fix the problem with a trick just by modifing this code at the very beginning
var fulltitle = (document.title);
var arr = fulltitle.split(':').slice(0, 1);
Since the part of title we need is always before the ':' , in this way I'm able to get the right result without making advanced comparisons.

Related

splitting an string list with proper aligning the string elements

Example Company 1,company ltd 2,company, Inc.,company Nine nine, ltd,company ew So here is example of the string, I want to split it like that it consider Company 1 as one company and company, Inc. as one, but here got situation in company, Inc. it condidering 2 companies while this logic. how can I resolve this? Lke with such strings company, Inc. I want to consider it one element only
const company = company.split(",");
Here the string can be anything, this is just example for the string, but it can be any name. So I am looking for generic logic which works for any string, having same structure of string.
Note $ ==(,) represents as separation point, kept to get clarity that from that point I need to separate the string
Object:
Example 1
{
_id: 5de4debcccea611e4d14d4d5
companies: One Bros. Inc. & Might Bros. Dist. Corp.$Pages, Inc.$Google Inc. Search$Aphabet Inc. tech.
}
Example 2
{
_id: 5de4debccc333611e4d14d4f5
companies: Google Comp. Inc.$Google Comp. Inc. Estd.$Tree, Ltd.$Tree, Ltd.
}
First I split on 'ompany' rather than 'company', because you have one instance of 'Company' with a capital C -- see the output of the first console log within a comment below.
Then I put things back together using reduce -- map is not the right choice here, as I need an array that is one fewer than the size of the fragments I generated. Then though since I need an array that corresponds to the number of strings we want to return, which is one fewer than the number of fragments, the first thing I do inside my reduce is ensure I do not look beyond the end of the array.
Then I split each fragment and pop off the last element, which just puts either "C" or "c" back together with "ompany". Then I replace any trailing ',c' from the next fragment with an empty string, and add the result to the company. Finally I add the entire result to the array I'm generating with reduce. See comment results at bottom. Also here it is on repl.it: https://repl.it/#dexygen/splitOnCompanyStringLiteral
This is a fairly concise way to do this but again if you can do anything to improve your data, you won't have to use such unnecessarily complicated code.
const companiesStr = "Company 1,company ltd 2,company, Inc.,company Nine nine, ltd,company ew";
const companySuffixFragments = companiesStr.split("ompany");
console.log(companySuffixFragments);
/*
[ 'C', ' 1,c', ' ltd 2,c', ', Inc.,c', ' Nine nine, ltd,c', ' ew' ]
*/
const companiesArr = companySuffixFragments.reduce((companies, fragment, index, origArr) => {
if (index < companySuffixFragments.length - 1) {
let company = fragment.split(',').pop() + 'ompany'
company = company + origArr[index + 1].replace(/,c$/, '');
companies.push(company);
}
return companies
}, []);
console.log(companiesArr);
/*
[ 'Company 1',
'company ltd 2',
'company, Inc.',
'company Nine nine, ltd',
'company ew' ]
*/
First change , with any other symbol. I am using & here and then split string with ,
var str= 'Company 1,company ltd 2,company, Inc.,company Nine nine, ltd,company ew';
str = str.replace(', Inc.','& Inc.');
/*str = str.replace(', ltd','& ltd');*/
console.log(str.split(',').map((e)=>{return e.replace('&',',').trim()}));
try with the below solution.
var str = ["company 1","company ltd 2","company", "Inc.","company Nine nine", "ltd","company ews"];
var str2 =str.toString()
var str3 = str2.split("company")
function myFunction(item, index,arr){if(item !=""){let var2 = item.replace(/,/g," ");var2 = "Company"+var2;arr[index]=var2;} }
str3.forEach(myFunction)
OUtput:
str3
(6) ["", "Company 1 ", "Company ltd 2 ", "Company Inc. ", "Company Nine nine ltd ", "Company ews"]
And remove the first element of the array.
As has been commented I'd try to get a more clean String so that you don't have to write "strange" code to get what you need.
If you can't do that right now this code should solve your problem:
let string = 'Company 1,company ltd 2,company, Inc.,company Nine nine, ltd,company
ew';
let array = string.split(',');
const filterFnc = (array) => {
let newArr = [],
i = 0;
for(i = 0; i < array.length; i++) {
if(array[i].toLowerCase().indexOf('company') !== -1) {
newArr.push(array[i]);
} else {
newArr.splice(newArr.length - 1, 1, `${array[i - 1]}, ${array[i]}`);
}
}
return newArr;
};
let filteredArray = filterFnc(array);

Highlight matching text

When posting users text to webpage (using Mongodb and node and js) I'm trying to highlight any of their text that matches a store name from an array of stores. The code for looping through the db and posting to page:
<% posts.forEach(function(post) { %>
<div class="post">
<h4 class="date">
<span><%= post.created.toDateString() %></span>
</h4>
<p class="post_text"><%- post.body %></p>
</div>
<% }); %>
I have some practice js console code I used to match words from an array but am having difficulty moving forward with putting the text back together with the highlighted word(s). 2 word store names are another issue...
var blogInput = "We went to target last night, also to publix";
var array1 = blogInput.split(" ");
var array2 = ["kroger", "lums", "marlows", "eats", "burger king",
"home", "wendys", "publix", "donut circus", "jewelry store",
"target"];
function getMatch(a, b) {
var matches = [];
for ( var i = 0; i < a.length; i++ ) {
for ( var e = 0; e < b.length; e++ ) {
if ( a[i] === b[e] ) {
var x = a[i];
matches.push( x );
}
}
}
return matches;
}
getMatch(array1, array2);
(2) ["target", "publix"]
Using this example I would then like to put the string sentence back together and post to page with 'target' and 'publix' text in blue. Any hints or words of wisdom would be helpful. Thanks!
You will need to surround the highlighted words in a span with an specific class that changes its color.
A function that could rebuild your post back with those spans could be similar to this one.
let blogInput = "We went to target last night, also to publix";
let blogWords = blogInput.split(" ");
let searchedWords = ["kroger", "lums", "marlows", "eats", "burger king",
"home", "wendys", "publix", "donut circus", "jewelry store",
"target"];
function getMatch(a, b) { ... }
let matchedWords = getMatch(blogWords, searchedWords);
let blogText = '';
blogWords.forEach(function(word) {
if (matchedWords.indexOf(word) != -1) {
blogText += ' <span class="highlight">' + word + '</span>';
} else {
blogText += ' ' + word;
}
});
// Remove leading space
blogText = blogText.trim();
You should then use the blogText as your post text. You will also need adding a CSS rule similar to this one:
span.highlight {
color: blue;
}
You don't need two loops, just work with blogInput as a string instead of splitting it into individual words and use indexOf (or includes) to determine if the keyword is in the string. This also solves the issues of trying to find store names with multiple words.
var blogInput = "We went to target last night, also to publix";
var array2 = ["kroger", "lums", "marlows", "eats", "burger king",
"home", "wendys", "publix", "donut circus", "jewelry store",
"target"];
// filter out any store names that aren't included in blogInput
var matches = array2.filter(function(store) {
return blogInput.indexOf(store) > -1;
});
You also may want to blogInput.toLowerCase() and store.toLowerCase() to resolve casing issues.
If you are targeting new browsers with ES6 support or using a transpiler like Babel you can simplify to:
const blogInput = "We went to target last night, also to publix";
const storeNames = ["kroger", "lums", "marlows", "eats", "burger king",
"home", "wendys", "publix", "donut circus", "jewelry store",
"target"];
// filter out any store names that aren't included in blogInput
var matches = storeNames.filter(store => blogInput.includes(store));

Multiple words search and calculation algorithm (Angular/Javascript)

I'm loading json file from database with two fields words and grade. Each word is graded for example true has 1 while lie has -1. Then i take input from text filed and i need to grade it based on grades from JSON file and then calculate score by summarizing the grades, but i just can't seem to find the way to do that. Words that are not in file are not being calculated.
I tried string.search match but it's to complicated and in the end i couldn't get result the way i wanted. I tried array searches same thing. I searched for on line solution, but no one has done anything similar so i can't copy it.
JSON
[
{"word":"true","grade":1},
{"word":"hate","grade":-1},
{"word":"dog","grade":0.8},
{"word":"cat","grade":-0.8}
]
String
"Dogs are wonderful but i prefer cats, cats, i can not lie although dog is a true friend".
The first thing I'd do is turn your JSON data into a map which can easily be searched - key would be the word, and value the grade:
var json = [
{"word":"true","grade":1},
{"word":"hate","grade":-1},
{"word":"dog","grade":0.8},
{"word":"cat","grade":-0.8}
];
var map = json.reduce(function(p,c){
p.set(c.word.toLowerCase(),c.grade);
return p;
}, new Map());
console.log(...map);
Then, its just a case of splitting your string, whilst also calculating the total score - again reduce can be used
var json = [
{"word":"true","grade":1},
{"word":"hate","grade":-1},
{"word":"dog","grade":0.8},
{"word":"cat","grade":-0.8}
];
var map = json.reduce(function(p,c){
p.set(c.word.toLowerCase(),c.grade);
return p;
}, new Map());
var input = "Dogs are wonderful but i prefer cats cats i can not lie although dog is a true friend";
var score = input.split(' ').reduce(function(p,c){
var wordScore = map.get(c.toLowerCase()) || 0;
return p + wordScore;
},0);
console.log(score);
Note that I have manually removed punctuation in the above input - I'll leave that as an exercise for you.
Also note that "cats" != "cat" so some of your words wont be found!
Let's first think of the algorithm. Two options:
Search and count the input string as many times as number of words in your JSON, or
Check each word in your input string against the JSON contents.
Since the JSON length is known and (I presume) shorter than the possible input string, I would tend to prefer option 2.
Now, after selecting option 2, you need to split the input string into words and create an array containing one word each entry of the array.
You can achieve this using the mystring.split(" ") method. This, of course, does not take into account punctuations, but you can handle this using the same method.
Now, you can add to each entry in your JSON a field to count the number of appearances of each entry in the JSON within the string.
Finally, you sum the product of the counters and the grade.
console.log((function(rules, str) {
var sum = 0;
Array.prototype.forEach.call(rules, function(rule) {
var match = str.match(rule.regexp);
match && (sum += str.match(rule.regexp).length * rule.grade);
console.log([rule.regexp, match&&match.length, rule.grade, match&&match.length * rule.grade, sum]);
});
return sum;
})([{
"regexp": /true/g,
"grade": 1
}, {
"regexp": /hate/g,
"grade": -1
}, {
"regexp": /dog/g,
"grade": 0.8
}, {
"regexp": /cat/g,
"grade": -0.8
}], "Dogs are wonderful but i prefer cats, cats, i can not lie although dog is a true friend"));
i use regexp rather than string, u can use string and convert to regex at run time, hope this would help

Is it possible to use code to transform a list in format 'list': "translation"?

I would like to be able to change a list of words in my coding, e.g.
var words= {
AFK Away from the keyboard 4U For you B4N By for now BBL Be back later BDAY Birthday CBA Can't be asked }
changed to this format:
var words= {
'AFK': "Away from the keyboard", '4U': "For you", 'B4N': "By for now", 'BBL': "Be back later", 'BDAY': "Birthday", 'CBA': "Can't be asked",
}
without having to change each word into the format manually using HTML/JavaScript. However I understand that this might not be possible, but I thought I'd see if anyone had an I idea on how to do it anyway. From what I read it looks as if I'll have to use Python and a Database, but I don't know anything about python what so ever really so I was hoping (probably vainly) that there is some HTML/JavaScript code that I haven't seen that solves this!
I found a similar question here, but it wasn't really what I wanted as it uses python: [turning data into a list
The thing I want to do is change all words in this format: e.g
AFK Away from the Keyboard
to a format with
'AFK': "Away from the keyboard",
the aim of this code is to translate text abbreviations to real English words which it is already doing, but in order to get a decent amount of words to translate I need to get words in the above format which would take forever if I formated each one individually. here is the rest of the code if that helps:
function replacer() {
var text= document.getElementById("textbox1").value;
for (var modifiers in translationwords){
text = text.replace(new RegExp('\\b' + modifiers + '\\b', 'gi'), translationwords[modifiers]); }
document.getElementById("textbox2").value=text;
document.getElementById("add").onclick= function storage() {
if(!document.cookie) document.cookie = "";
document.cookie = document.cookie +"<li>"+document.getElementById("textbox1").value+ ":"+"</li>";
document.cookie = document.cookie +"<li>" + document.getElementById("textbox2").value+ "</li>";
document.getElementById("array").innerHTML= document.cookie;
}
}
function textdelete(x) {
if (x.value=="ENTER TRANSLATION HERE"){
x.value="";
};
}
Thank You
If your words were in a string to begin with, then it would be arguably possible to translate it into an object the way you want it, but it might not be the most accurate translation.
Since it looks like all the abbreviations are all upper case and without spaces, we can look through the string, and set the all caps/numbers 'words' as properties, with the following string of text as the value. In javascript, something like this would work.
//set words as string
var string = "AFK Away from the keyboard 4U For you B4N By for now BBL Be back later BDAY Birthday CBA Can't be asked";
// create empty dictionary for storage
var dictionary = {};
// use regex to find all abbreviations and store them in an array
var abbreviations = string.match(/[A-Z0-9]+(?![a-z])\w/g);
// returns ["AFK", "4U", "B4N", "BBL", "BDAY", "CBA"]
// use regex to replace all abbreviations with commas...
englishWords = string.replace(/[A-Z0-9]+(?![a-z])\w/g, ',');
// Edit (see below):
englishWords = englishWords.replace(/\W,\W/g,',');
// End edit
// then split string into array based on commas
englishWords = englishWords.split(',').slice(1);
// finally loop over all abbreviations and add them to the dictionary with their meaning.
for(var i = 0; i < abbreviations.length; i++){
dictionary[abbreviations[i]] = englishWords[i];
}
Edit: the above solution still might have white space at the beginning or end of each english string. You can add this line of code just before splitting the string to remove the white space.
englishWords = englishWords.replace(/\W,\W/g,',');
Assuming you have the data as a list in python, or as a string, or that you can get it into that format:
>>> lst = 'AFK Away from the keyboard 4U For you B4N By for now BBL Be back later BDAY Birthday CBA Can\'t be asked'.split()
>>> lst
['AFK', 'Away', 'from', 'the', 'keyboard', '4U', 'For', 'you', 'B4N', 'By', 'for', 'now', 'BBL', 'Be', 'back', 'later', 'BDAY', 'Birthday', 'CBA', "Can't", 'be', 'asked']
Further assuming that the keywords are always in all-uppercase and that the values are never in all uppercase, and that there are no duplicate keys, you can create the following dictionary:
>>> keys = [x for x in lst if x.upper() == x]
>>> {keys[i]:' '.join(lst[lst.index(keys[i]):lst.index(keys[i+1])]) for i in range(len(keys)-1)}
{'BDAY': 'BDAY Birthday', 'BBL': 'BBL Be back later', '4U': '4U For you', 'B4N': 'B4N By for now', 'AFK': 'AFK Away from the keyboard'}
Let's say that this is your list of words:
["AFK","Away","from","the","keyboard","4U","For","you","B4N","By","for","now","BBL","Be","back","later","BDAY","Birthday","CBA","Can't","be","asked"]
Now, we can change that into an object simply by using regular JavaScript without any database or back-end langauge like Python.
var list = ["AFK","Away","from","the","keyboard","4U","For","you","B4N","By","for","now","BBL","Be","back","later","BDAY","Birthday","CBA","Can't","be","asked"] /*What we have*/;
var code = {} /*What we want*/;
var currKey = null /*Our current key*/, hasBeenSet = false /*Whether or not the property at our current key has been set*/;
for (var i = 0; i < words.length; i++) {
//If the current word is an abbreviation...
if (words[i] === words[i].toUpperCase()) {
currKey = words[i]; //We set currKey to it
hasBeenSet = false; //We make hasBeenSet false
}
//Otherwise, if the property at current key has been set, we add on to it.
else if (hasBeenSet) code[currKey] += " "+words[i];
//Otherwise, the property at current key hasn't been set...
else {
code[currKey] = words[i]; //We set the property to the current word
hasBeenSet = true; //We make hasBeenSet true
}
}
code //{ AFK: "Away from the keyboard", 4U: "For you", B4N: "By for now", BBL: "Be back later", BDAY:"Birthday", CBA:"Can't be asked" }

Can I 'match' values in 2 arrays and then use the subsequent 'matched' value?

I'm trying to achieve the following though with my intermediate JavaScript skills I'm not sure if this is possible.
This is related in part to this question.
Now I have 2 arrays
a) Has the various language in (e.g. "en-GB", "fr", "de" etc)
b) Has a suffix of a URL based on the browser language above (e.g. "fr/","de/","uk/")
What I am trying to achieve is:
1) User hits a page, browser detects which browser it is using from the array (a)
2) Depending on what the browser is based on (a), it then searches through (b) and if they match, e.g. if the language is "fr" it will use the suffix "fr/" from the array in (b).
3) It will then add this suffix to a top level domain (which is always constant)
Is this even possible to achieve (I'm sure it is)? Can it be done purely via JavaScript (or JQuery)? How would I go about doing this?
Here's some of the code I have so far:
var IAB_Array = new Array("de-at","nl-be","fr-be","da","de","hu","en-ie","ga","es","fr","it","nl","no","pl","en","en-GB","en-US","en-gb","en-us"); //language array
var IAB_suffix = new Array("at/","be-nl/","be-fr","den/","de/","hu/","ie/","es/","fr/","it/","nl/","nor/","pl/","uk/"); //URL suffix Array
var IAB_lang = "en-GB"; //default language
var IAB_link = "http://www.mysitegoeshere/";
if(navigator.browserLanguage) IAB_lang = navigator.browserLanguage; //change lang if detection supported
if(window.navigator.language) IAB_lang = window.navigator.language; //change lang if detection supported
function IAB_Lang_detect () { //execute search
for (var i=0;i<IAB_Array.length;i++) {
if(IAB_Array[i]==IAB_lang) {
document.write(IAB_Array[i]); //output matched array value
}
}
return false;
}
var IAB_URL = ""+IAB_link+IAB_suffix[1]+""; //this is the resulting URL
document.write(IAB_URL);
IAB_Lang_detect ();
I hope someone can help as I'm a little confused! It's more so the matching the values from the 2 arrays and then subsequently selecting the correct suffix that I'm having trouble with.
Thanks
(function () {
"use strict";
var lang_map = {
"de-at": "at/",
"nl-be": "be-nl/",
"fr-be": "be-fr",
"da": "den/",
"de": "de/",
"hu": "hu/",
"en-ie": "ie/",
"ga": "ie/",
"es": "es/",
"fr": "fr/",
"it": "it/",
"nl": "nl/",
"no": "nor/",
"pl": "pl/",
"en": "uk/",
"en-GB": "uk/",
"en-US": "uk/",
"en-gb": "uk/",
"en-us": "uk/"
},
lang = (navigator && navigator.browserLanguage) || (window.navigator && window.navigator.language) || "en-GB";
window.location = "http://www.mysitegoeshere/" + lang_map[lang];
}());
I'd do it differently and use an object:
var IAB_Object = { "it-It": "it/", "en-Gb": "en/" ....}
if(IAB_Object.hasOwnProperty(IAB_lang)){
//you have a match, the suffix is
var suffix = IAB_Object[IAB_lang];
}else{
//you don't have a match use a standard language
}
I probably wouldn't use arrays for this at all. You can use an object:
var IABInfo = {
"de-at": "at/",
"ln-be": "be-nl/",
// ...and so on
};
Then index directly into that object:
var value = IABInfo[IABLang]; // Where IABLang contains a string, like "de-at"
So:
var suffix = IABInfo[IABLang];
if (suffix) { // Did we have it?
document.write(suffix);
}
This works because all JavaScript objects are free-form key/value maps. Here's a simpler example:
var lifeTheUniverseAndEverything = {
answer: 42,
question: "?"
};
You can look up a property either using dotted notation with a literal, or by using square bracket ([]) notation with a string. So all four of these output exactly the same thing:
// 1. Dotted notation with a literal:
console.log("The answer is " + lifeTheUniverseAndEverything.answer);
// 2. Bracketed notation with a string
console.log("The answer is " + lifeTheUniverseAndEverything["answer"]);
// 3. The string needn't be a literal, it can come from a variable...
var name = "answer";
console.log("The answer is " + lifeTheUniverseAndEverything[name]);
// 4. ...or indeed any expression:
console.log("The answer is " + lifeTheUniverseAndEverything["a" + "n" + "swer"]);
So by making your IAB info a map in an object literal, you can make it much easier to look things up: Just use bracketed notation with the desired language code.

Categories