We have multiple records, each record has an asset list. The assest list consists of multiple client names and sites (name-site, name-site_2, name2-site_3, name3-site_4).
I'm trying to split the asset list to end up with two lists, one of all of the names and one of all of the sites (I only want to show what's unique in each list). I'm splitting at the comma and then split again at the hyphen
What I've created below works, but recently I've encountered some client-site combos that have an extra hyphen, which breaks my solution (I initially split the string by comma and then split the substrings by hyphen). To add another wrinkle, the extra hyphen is not always in the same spot, depending on the category it could be the first hyphen (in the client name) or second hyphen (in the site) that needs to be ignored, fortunately, this is consistent by category (if category == "animals").
For category animals if there are two hyphens I need to ignore the first.
For category fruit if there are two hyphens I need to ignore the second.
Any ideas?
Example asset lists:
category == "animals"
if there is two hyphens, I need to split at the second.
assetList ="fish-mark, cat-jim, blue-dog-henry, red-bird-bill, green-snake-larry"
category == "fruit"
if there is two hyphens, I need to split at the first.
assetList = "lime-henry, lemon-susan, banana-bob-nelson, apple-rick-jones, pineapple-sam-smith"
Below is my code:
var assetList = "fish-mark, cat-jim, blue-dog-henry, red-bird-bill, green-snake-larry";
var count = (assetList.match(/-/g) || []).length;//counts the hyphens (client-siteId combo)
var splitObj = {};
var comboObj = {};
var clientObj = {};
var siteObj = {};
var mainSplitObj = {};
var allClient = '';
var allSite = '';
mainSplitObj = assetList.split(', ');
for (var i = 0; i < count; i++) {
splitObj["split"+i] = mainSplitObj[0+i]; //puts the client-siteID into a substring
comboObj["combo"+i] = splitObj["split"+i].split('-'); //splits the client-siteID at the dash
clientObj["client"+i] = comboObj["combo"+i][0]; //puts the client name in a substring
siteObj["site"+i] = comboObj["combo"+i][1]; //puts the siteid in a substring
allClient += clientObj["client"+i] +";"+ ' '; //cumulatively adds client substrings to allClient variable
allSite += siteObj["site"+i] +";" + ' '; //cumulatively adds site substrings to allSite variable
}
tempC = allClient.split(",")
uniqueClient = []
for (var i = 0; i < tempC.length; i++) {
isIn = 0
for (var j = 0; j < uniqueClient.length; j++) {
if (tempC[i] == uniqueClient[j]) {
isIn = 1
}
}
if (isIn == 0) {
uniqueClient.push(tempC[i])
}
}
tempS = allSite.split(",")
uniqueSite = []
for (var i = 0; i < tempS.length; i++) {
isIn = 0
for (var j = 0; j < uniqueSite.length; j++) {
if (tempS[i] == uniqueSite[j]) {
isIn = 1
}
}
if (isIn == 0) {
uniqueSite.push(tempS[i])
}
}
Here is a way to do at with indexOf and lastIndexOf. It splits on the last instead of the second hyphen which in your case gives you the same results.
// Split on first hyphen
var list = ["lime-henry", "lemon-susan", "banana-bob-nelson", "apple-rick-jones", "pineapple-sam-smith"];
for (var i = 0; i < list.length; i++) {
var delimiterIndex = list[i].indexOf("-");
var item = list[i];
var left = item.substring(0, delimiterIndex);
var right = item.substring(delimiterIndex + 1, item.length);
console.log(left, right);
}
/* outputs
* lime henry
* lemon susan
* banana bob-nelson
* apple rick-jones
* pineapple sam-smith
*/
// Split on last hyphen
var list = ["fish-mark", "cat-jim", "blue-dog-henry", "red-bird-bill", "green-snake-larry"];
for (var i = 0; i < list.length; i++) {
var delimiterIndex = list[i].lastIndexOf("-");
var item = list[i];
var left = item.substring(0, delimiterIndex);
var right = item.substring(delimiterIndex + 1, item.length);
console.log(left, right);
}
/* outputs
* fish mark
* cat jim
* blue-dog henry
* red-bird bill
* green-snake larry
*/
Related
I'm creating a kid's learning tool that has a form which matches 4 letters to a word.
I want to count the number of character matches in a word. But it counts duplicates of letters as 2 instead of 1. For example if the word is "loot", and the user submits "flop", the matching letters are 3 instead of 2, because it's counting "o" twice. How do I fix this? Many thanks
function countMatching(str1, str2) {
var c = 0;
for (var i = 0; i < str1.length; i++) {
if (str2.includes(str1[i]))
c += 1;
}
matchingLetters = c;
}
I made an alternative version of #cmgchess' answer, which creates an array of the actual solution of letters to still guess, and removes each letter as it is encountered in the entered solution.
let matchingLetters;
function countMatching(str1, str2) {
var c = 0;
str1Arr = str1.split('');
for (var i = 0; i < str2.length; i++) {
if (str1Arr.includes(str2[i])) {
c += 1;
str1Arr.splice(str1Arr.indexOf(str2[i]), 1);
}
}
matchingLetters = c;
}
countMatching('loot', 'boot')
console.log(matchingLetters)
A possible approach using Sets.
I converted the string into a set and back to an array
so if str1 is loot then str1Set will be ['l','o','t']. The rest is the same
let matchingLetters;
function countMatching(str1, str2) {
var c = 0;
str1Set = [...new Set([...str1])]
str2Set = [...new Set([...str2])]
for (var i = 0; i < str1Set.length; i++) {
if (str2Set.includes(str1Set[i]))
c += 1;
}
matchingLetters = c;
}
countMatching('loot', 'flop')
console.log(matchingLetters)
function countMatching(str1, str2) {
var c = [];
for (var i = 0; i < str1.length; i++) {
if (str2.includes(str1[i]))
if (!c.includes(str1[i])) c.push(str1[i])
}
matchingLetters = c.length;
}
I'm writing on a tab, so there might be mistakes
What I did is that I made c to be a list that contains each character shared between the two strings. And a new character is pushed into c only if the character doesn't exist in it, hence making it a list of unique characters.
Then you can get the length of the list or you can even use the list for other purposes
I am coding search on a webpage which searchs all files, which the webpage consists of.
I iterate through every file and save all words to array of strings.
For example: var array = ["these","are","some","random","words","on","a","webpage"]
The search engine works this way: e.g. user type "s" and if any word from array contains this letter, the word is displayed as a result. In this case the results would be: "these", "some", "words"
The problem is that I have like 30 files in which I search and in each file there is on average 500 words so the search is slow.
letter search (e.g. "s") ~ 4 seconds
letter search (e.g. "se") ~ 2.1 seconds
letter search (e.g. "sea") ~ 1.9 seconds
letter search (e.g. "sear") ~ 1.7 seconds...
I iterate through the array with for-cycle and I think that is the biggest problem. So what is the fastest way to find if searched word is in array of strings and compare also not full matches?
EDIT:
On the webpage it looks like this e.g.:
Searched word: "sear"
Results:
Intro (name of page; clickable url link)
...you can search in this page... (sentence with words around the searched word)
Code explanation:
iterating through files
remove html characters and other special characters and save words from file to array of strings
compare words from file with words which user search for
save sentence with the searched word to a sentence variable
save sentence to an object (this object is later iterated in .html file and the sentences are displayed at webpage)
words typed by user which are going to be searched are in variable words
Here is my code.
var searchIndexPromise;
var searchAppModule = angular.module("searchApp", []);
searchAppModule.run(function($rootScope, $http){
var globalSearch = $rootScope.globalSearch = {
query: "",
results: [],
open: function(){
window.location.href = "#!/51_00_Search";
globalSearch.search(globalSearch.query);
},
search: function(find) {
if(!searchIndexPromise) searchIndexPromise = $http.get("searchIndex.json").then(function(response){
return response.data;
});
console.log("searching", find);
searchIndexPromise.then(function(searchIndex){
var temp = [];
globalSearch.results = [];
var words = find.split(' ');
if (words < 1) {
return;
}
for (var key in searchIndex) {
for (var option in searchIndex[key]) {
for(var i=0; i < words.length; i++) {
if (key.includes(words[i].toLowerCase())) {
var name = searchIndex[key][option].name;
var page = searchIndex[key][option].page;
var word = words[i];
var count = 0;
for (var j = 0; j < temp.length; j++) {
if (temp[j].name == name && temp[j].word == word) {
break;
}
count++;
}
if (count == temp.length) {
temp.push({ name : name, page : page, word : word });
}
}
}
}
}
if (words.length < 2) {
globalSearch.results = temp;
}
else {
for (var i = 0; i < temp.length; i++) {
var count = 0;
var compare = temp[i];
for (var j = 0; j < temp.length; j++) {
if (compare.name == temp[j].name) {
if (globalSearch.results.indexOf(temp[j]) == -1) {
count++;
}
}
}
if (count == words.length) {
globalSearch.results.push(temp[i]);
}
}
}
//sentences
const pagesLoad = require("./pages.js");
globalSearch.pages = [];
for (var result in globalSearch.results) {
var page = globalSearch.results[result].page.substring(3);
if ((page + ".html" in pagesLoad)) {
var nameOfPage = page + ".html";
}
if ((page + ".md" in pagesLoad)) {
var nameOfPage = page + ".md";
}
var regex = /(<([^>]+)>)|\n|\#|\(|\)|\*|\-|[^\w\s!?]|\n| +(?= )/ig, data = pagesLoad[nameOfPage].src.replace(regex, " ");
var string = data.split(" ");
string = string.filter(Boolean);
let lowerString = string.map((item) => {
return item.toLowerCase();
});
//this part is slowing down the search
for (var i = 0; i < lowerString.length; i++) {
for (var j = 0; j < words.length; j++) {
if (lowerString[i].includes(words[j].toLowerCase())) {
var sentence = "...";
for (var k = i - 6; k < i + 6; k++) {
if (lowerString[k] == null) {
continue;
}
sentence = sentence + string[k] + " ";
}
sentence = sentence.slice(0, -1);
sentence += "...";
globalSearch.pages.push({page: globalSearch.results[result].page, sentence: sentence});
}
}
}
}
})
}
};
});
I want to loop through set of strings and break them into sub strings by removing two characters and storing them into an array.
lets say two stings are returned, the number of characters will always be multiple of 2.
AADDFFCC
GGDD
The first string will give me AADDFF,AADD,AA
The second string will give me GG.
i want to store all sub string into a single array.
So based upon above example i should end up with.
subString = ["AADDFF","AADD","AA","GG"]
This is my incomplete attempt.
var StringArray = ["AADDFFCC","GGDD"] //this could be indefinite length
var subString = [];
var StringArrayLength = StringArray.length;
var loopCurrentString;
var loopCurrentSubString
for (var i = 0; i < StringArrayLength; i += 2) {
loopCurrentString = StringArray[i];
loopCurrentSubString = loopCurrentString.substring(0, loopCurrentString.length - 2);
}
for (var i = 0; i < StringArrayLength; i ++) {
//get element
loopCurrentString = StringArray[i];
//add substrings in the array
while(loopCurrentString.length>2){
loopCurrentSubString = loopCurrentString.substring(0, loopCurrentString.length - 2);
substring.push(loopCurrentSubString);
}
}
Here is an implementation with nested for loops:
const data = ["AADDFFCC", "GGDD"]
const endResult = []
for (var i = 0; i < data.length; i++) {
for (var j = 0; j < data[i].length; j += 2) {
if (j != 0)
endResult.push(data[i].slice(0, data[i].length - j))
}
}
console.log(endResult)
You could do this by using reduceRight to chunk the array into the desired pieces starting from the right side:
const data = ["AADDFFCC","GGDD"]
const chunkBy = (arr, by=2) => [...arr].reduceRight((r,c,i,a) =>
((((a.length-1) - i)%by == 0 && a.length-1 != i) ?
r.push(a.slice(0, i+1).reduce((r,c) => `${r}${c}`)) :
0, r),[])
console.log(data.reduce((r,c) => [...chunkBy(r), ...chunkBy(c)]))
And on the end stitch them together via ES6 spread operator.
Let's say I have an array:
[
"I want **a dog**",
"**A dog** is here",
"Pet **a dog**",
"A **red cat**",
"**red cat** is cute"
...
]
How do I figure out what the duplicate phrases are, not just words?
For example, I'd like "a dog" and "red cat" to be returned.
Most existing posts I found are only about getting individual words, not phrases (multiple words).
You're giving us too little information. I'm assuming you're splitting by spaces. ES6 to the rescue :). Sets have O(1) lookup for when you're looking for repeated phrases.
edit: Just realized that you can cut down space complexity by a ton with some small modifications. If you want me to do that, give me a shoutout.
const buildAllPhrases = sentence => {
const splitSentence = sentence.split(" ")
const phraseSize = splitSentence.length
const allPhrases = []
for (let i = phraseSize; i > 0; i--) {
for (let y = 0; y + i <= phraseSize; y++) {
allPhrases.push(splitSentence.slice(y, y + i))
}
}
return allPhrases.map(phrase => phrase.join(" "))
}
const findRepeats = sentences => {
const allPhrases = new Set()
const repeatedPhrases = new Set()
let phrases
sentences.forEach(phrase => {
phrases = buildAllPhrases(phrase)
phrases.forEach(subPhrase => {
if (allPhrases.has(subPhrase)) {
repeatedPhrases.add(subPhrase)
} else {
allPhrases.add(subPhrase)
}
})
})
return [...repeatedPhrases]
}
const sample = [
"I want **a dog**",
"**A dog** is here",
"Pet **a dog**",
"A **red cat**",
"**red cat** is cute"
]
findRepeats(sample)
//['dog**', '**a dog**', '**a', '**red cat**', '**red', 'cat**', 'is']
This is not final version of the javascript function, and it can be optimized further. Few changes may also be required, but it can be starter for your requirement.
function GetPhrases(stringsArray) {
//Array to split your string into words.
var jaggedArray = [];
//Array to keep indexes of strings where 2 matching words are found together.
var newArray = [];
var phrases = [];
//Loop through your array
for (var ic = 0; ic < stringsArray.length; ic++) {
//Convert every item to array of strings
var items = (stringsArray[ic]).split(" ");
for (var it = 0; it < items.length; it++)
items[it] = items[it].toLowerCase();
//Push the array of words to main array
jaggedArray.push(items);
}
//console.log(jaggedArray);
// Loop through the main array
for (var iLoop = 0; iLoop < jaggedArray.length; iLoop++) {
// For every item in main array, loop through words in that item.
for (var ik = 0; ik < jaggedArray[iLoop].length; ik++) {
var currentWord = jaggedArray[iLoop][ik];
// For every word, check its existence in the main array in all items coming after current item.
for (var il = iLoop + 1; il < jaggedArray.length; il++) {
// Find the index in the string.
var indexOfFind = jaggedArray[il].indexOf(currentWord);
if (indexOfFind > 0) {
// if matching index is more than 0, find if the word before this word also matches.
var indexofPrevWord = jaggedArray[il].indexOf(jaggedArray[iLoop][ik - 1]);
if ((indexofPrevWord >= 0) && (indexofPrevWord == (indexOfFind - 1)))
if (newArray.indexOf(il + " - " + iLoop) < 0)
newArray.push(il + " - " + iLoop);
// if matching index is more than 0, find if the word after this word also matches.
var indexofNextWord = jaggedArray[il].indexOf(jaggedArray[iLoop][ik + 1]);
if (indexofNextWord >= 0 && (indexofNextWord == (indexOfFind + 1)))
if (newArray.indexOf(il + " - " + iLoop) < 0)
newArray.push(il + " - " + iLoop);
}
else if (indexOfFind = 0) {
// if matching index is more than 0, find if the word after this word also matches.
var indexofNewWord = jaggedArray[il].indexOf(jaggedArray[iLoop][ik + 1]);
if (indexofNewWord >= 0 && (indexofNewWord == (indexOfFind + 1)))
if (newArray.indexOf(il + " - " + iLoop) < 0)
newArray.push(il + " - " + iLoop);
}
}
}
}
//newArray will store indexes of those string arrays in jagged array which has a matching sequence of atleast 2 words.
//console.log(newArray);
//Loop through newArray
for (var itl = 0; itl < newArray.length; itl++) {
var item = newArray[itl];
var values = item.split(" - ");
var firstArrayItem = jaggedArray[values[0]];
var secondArrayItem = jaggedArray[values[1]];
var phraseStartPoint = [];
//for every word in firstItem
for (var iy = 0; iy < firstArrayItem.length - 1; iy++) {
var t = iy + 1;
// check if that word and next word exist in second array
if (secondArrayItem.toString().indexOf(firstArrayItem[iy] + "," + firstArrayItem[t]) >= 0) {
// if they do exist, get the indexes of these and store in local array, if they are not there, since we do not want repeating words later.
if (phraseStartPoint.indexOf(iy) < 0)
phraseStartPoint.push(iy);
if (phraseStartPoint.indexOf(t) < 0)
phraseStartPoint.push(t);
}
}
var str = "";
// Prepare the phrase from the local array and push into phrases array, if it not exists there.
for (var ifinalLoop = 0; ifinalLoop < phraseStartPoint.length; ifinalLoop++) {
str = str + firstArrayItem[phraseStartPoint[ifinalLoop]] + " ";
}
if (phrases.indexOf(str) < 0)
phrases.push(str);
}
return phrases;
}
var stringsArray = [
"I want a dog",
"A dog is here",
"Pet a dog is cute",
"A red cat is here",
"red cat is cute"
];
var result = GetPhrases(stringsArray);
// Print the phrases array.
for (var iPhrase = 0; iPhrase < result.length; iPhrase++) {
console.log(result[iPhrase]);
}
with regex you can detect duplicates in strings.
According to this regex:
(?:.*?)(\b\w.{3,}\b)(?:.*?)(\1) ,
it only works if you're looking for twice the same pattern.
note: you can replace 3 in {3,} by any other integer and see the changes.
This paramater contrains the minimal string lenght you're looking for twice.
This is a challenge for coderbyte I thought I'd try to do it using a different method for solving it than loops, objects. It passed but it isn't perfect. The directions for the challenge are:
Have the function LetterCountI(str) take the str parameter being passed and return the first word with the greatest number of repeated letters. For example: "Today, is the greatest day ever!" should return greatest because it has 2 e's (and 2 t's) and it comes before ever which also has 2 e's. If there are no words with repeating letters return -1. Words will be separated by spaces.
function LetterCountI(str){
var wordsAndLetters = {};
var count = 0;
var finalword;
str = str.split(" ");
for(var i = 0; i < str.length; i++){
wordsAndLetters[str[i]] = wordsAndLetters[str[i]] || 0;
}
function countWordLetters(strs){
strs = strs.split("");
var lettercount = {};
for(var i = 0; i <strs.length; i++){
lettercount[strs[i]] = lettercount[strs[i]] || 0;
lettercount[strs[i]]++;
}
return lettercount;
}
for(var words in wordsAndLetters){
wordsAndLetters[words] = countWordLetters(words);
var highestLetterFrequency = wordsAndLetters[words];
for(var values in highestLetterFrequency){
if(highestLetterFrequency[values] > count){
count = highestLetterFrequency[values];
finalword = words;
}
if(count !== 1){
return finalword;
}
}
}
return -1;
}
LetterCountI("today is the greatest day ever!");
Sorry if some of the variable names are confusing I've been up for far too long trying to figure out what I did wrong. If you use the parameters at the bottom of the code it returns 'greatest' like it should however change the parameters to
LetterCountI("toddday is the greatttttest day ever!");
and it logs 'toddday' when it should log 'greatttttest'. Is my code completely wrong? I realize if the parameters were ("caatt dooog") it should log 'caatt' since there are 4 recurring letters but I'm not worried about that I just am concerned about it finding the most recurrence of one letter(but by all means if you have a solution I would like to hear it!). Any changes to the variables if needed to make this code more readable would be appreciated!
The problem with your code is the positioning of the following section of code:
if(count !== 1){
return finalword;
}
Move it from where it currently is to just before the return -1, like so:
for(var words in wordsAndLetters){
wordsAndLetters[words] = countWordLetters(words);
var highestLetterFrequency = wordsAndLetters[words];
for(var values in highestLetterFrequency){
if(highestLetterFrequency[values] > count){
count = highestLetterFrequency[values];
finalword = words;
}
}
}
if(count !== 1){
return finalword;
}
return -1;
The problem with your original code is that your were returning the first word that had repeating characters, which meant your code didn't get far enough to check if any subsequent words had more repeating characters.
Also, just for fun, here is my alternative solution.
Here you go
Array.prototype.getUnique = function(){
var u = {}, a = [];
for(var i = 0, l = this.length; i < l; ++i){
if(u.hasOwnProperty(this[i])) {
continue;
}
a.push(this[i]);
u[this[i]] = 1;
}
return a;
}
function LetterCountI(str){
var temp = str.split(" ");
var final = '', weight = 0;
for(var i = 0; i < temp.length; ++i) {
var word = temp[i].split("");
if(word.getUnique().length < word.length) {
var diff = word.length - word.getUnique().length;
if(diff > weight){
weight = diff;
final = temp[i];
}
}
}
return final;
}
console.log(LetterCountI("Catt dooog"));
console.log(LetterCountI("toddday is the greatttttest day ever!"));
Viva LinQ !!!!!
var resultPerWord = new Dictionary<string, int>();
var S = "toddday is the greatttttest day ever!";
foreach(var s in S.Split(' '))
{
var theArray =
from w in s
group w by w into g
orderby g.Count() descending
select new { Letter = g.Key, Occurrence = g.Count() };
resultPerWord.Add(s, theArray.First().Occurrence);
}
var r = "-1";
if (resultPerWord.Any(x => x.Value >1))
{
r = resultPerWord.OrderByDescending(x => x.Value).First().Key;
}