Calculate occurrence of characters in each loop - javascript

I need to calculate the occurrence of all characters in a for each loop, the base is inputs:
Object {SchriftEins: "abc ", SchriftZwei: "123"}
I'm having this code:
//go through every input
$.each(inputs,function(i,el){
var schrift = i;
var string = el;
// splitt string
var string_as_array = string.split("");
//count the occurrence of every char
$.each(string_as_array,function(i, el){
// here is the problem:
arr[schrift][el] = arr[schrift][el] + 1 || 1;
});
console.log(arr);
});
My problem is, that the calculation is wrong. I assume because of the nested each loop. How can i modify this code to get the right results?

Rather than splitting strings and using jQuery, you can use the built-in iteration and loop over the string. It is a fair bit simpler and should be much faster.
To combine the character counts from every word:
var characterCount = {};
Object.keys(inputs).forEach(function (item) {
var value = inputs[item];
for (var i = 0; i < value.length; ++i) {
var char = value[i];
characterCount[char] = (characterCount[char] || 0) + 1;
}
});
Or, separately (as shown in your example):
var characterCount = {};
Object.keys(inputs).forEach(function (item) {
var value = inputs[item];
for (var i = 0; i < value.length; ++i) {
var char = value[i];
// This will get existing counts, increment the proper field, and replace.
// It is a somewhat naive solution and could be improved.
var counts = characterCount[item] || {};
counts[char] = (counts[char] || 0) + 1;
characterCount[item] = counts;
}
});

Related

JavaScript: How to count repetitive letters in a string and output the numbers right after?

I am trying to display an output like this a3b2c4d3 for a string aaabbccccddd.
I tried the code below but didn't get the desired result.
var countLetters = "aaabbccccddd";
console.log("countLetters.length --->" + countLetters.length);
var countNumberLetter = 0;
var i;
var a;
for (i = 0; i < countLetters.length; i++) {
if (countLetters[i] == countLetters[i + 1]) {
countNumberLetter = countNumberLetter + 1;
}
}
console.log("countNumberLetter--------->" + countLetters[i] + countNumberLetter);
Use two loops. Use an outer while to loop the string. Whenever a new letter is encountered, use the for loop to increment count as the long as the letters belong to the same sequence. When done increment the outer counter (i) to get to the next letter:
var countLetters = "aaabbccccddd";
var result = '';
var i = 0;
while (i < countLetters.length) {
// iterate until current letter, and counted letter are not equal, increment count
for (var count = 1; countLetters[i] === countLetters[i + count]; count++) {}
// add current letter and count to string
result += countLetters[i] + count;
i += count; // increment outer counter - i
}
console.log(result);
Another solution that uses a String.match() with a regex to get an array of letter sequences. Then maps each sequence to letter + count, and joins them back to a string:
var countLetters = "aaabbccccddd";
var result = countLetters.match(/(\w)\1+/g) // match sequences of the same letter
.map((s) => s[0] + s.length) // map each sequence to letter with count
.join(''); // join back to a string
console.log(result);
var hashMap = {};
var countLetters = "aaabbccccddd";
countLetters.split("").forEach((letter) => {
if(!hashMap[letter]) {
hashMap[letter] = 0;
}
hashMap[letter] = hashMap[letter]+1;
})
var string ='';
for(var i in hashMap) {
var val = hashMap[i];
string += i + val;
}
console.log("countNumberLetter--------->",string);
const object = {};
const string = "aaabbccccddd";
// To iterate over string
for(let i = 0; i < string.length; i++){
// if the object has that alphabet just increment it
if(object.hasOwnProperty(string.charAt(i))){
++object[string.charAt(i)];
}else{
// else create a key to the new alphabet and give it a value 1
object[string.charAt(i)] = 1;
}
}
let finalString = "";
// To iterate over the object
for(let key in object){
finalString += key; // concatenate the key
finalString += object[key]; // concatenate the value
}
console.log(finalString);
My solution has two loops one to iterate over the string and store the alphabet and there count in an object ( you can use hashmap as well ).
The 2nd loop is to iterate over object so that we can make the desired string.

JavaScript Permutation Issuse

I am working on permutation str using recursive, but it can not get out of the for loop.
Can anyone help for this code?
Thank you in advance.
var permutations = [];
var words = [];
function getPerms(str) {
if(str.length == 0) {
permutations.push("");
return permutations;
}
var first = str.charAt(0);//get the first char
var reminder = str.slice(1);//remove the first char
words = getPerms(reminder);
for(var i = 0; i < words.length; i++) {
for(var j = 0; j <= words[i].length; j++) {
var s = insertCharAt(words[i], first, j);
permutations.push(s);
}
}
return permutations;
}
function insertCharAt(word, c, i) {
var start = word.slice(0, i);
var end = word.slice(i);
var result = start + c + end;
return result;
}
console.log(getPerms("abc"));
Your code is fine, except for one issue:
The variables permutations should not be a global variable. You can clearly see this is wrong, by looking at permutations.push(""). This is fine as a temporary result in the deepest level of recursion, but obviously this should not be present in the final result. Yet, because permutations is global, and you never remove anything from it, permutations will keep this "".
The problem gets worse because words gets the permutations reference from the recursive call, and so they point to the very same array! So not only are all previous results iterated, but with an extra character add to them they are pushed again into permutations, which is the same array as words giving you an endless loop: you add to the array you are iterating, and so never get to the end.
The solution is simple:
Make permutations a variable local to the getPerms function. And why not do the same for words when you are at it.
function getPerms(str, depth=0) {
var words = [];
var permutations = [];
if(str.length == 0) {
permutations.push("");
return permutations;
}
var first = str.charAt(0);//get the first char
var reminder = str.slice(1);//remove the first char
words = getPerms(reminder, depth+1);
for(var i = 0; i < words.length; i++) {
for(var j = 0; j <= words[i].length; j++) {
var s = insertCharAt(words[i], first, j);
permutations.push(s);
}
}
return permutations;
}
function insertCharAt(word, c, i) {
var start = word.slice(0, i);
var end = word.slice(i);
var result = start + c + end;
return result;
}
console.log(getPerms("abc"));
Be sure to check these solutions offered for this problem.
The problem is probably at the last call you return permutations and then assign it to words (what you did here is like words = permutation), but this is not an assignement by copy, but by reference (because words and permutations belongs to the same scope + they are array), so from the last call they are the same object (and when the code unstack previous call, they are now the same object). To illustrate, execute the following code. You will see, when you modify words you modify permutations:
var permutations = [];
var words = [];
function getPerms(str) {
if(str.length == 0) {
permutations.push("");
return permutations;
}
var first = str.charAt(0);//get the first char
var reminder = str.slice(1);//remove the first char
words = getPerms(reminder);
words.push("foo");
console.log( permutations);
return permutations;
}
function insertCharAt(word, c, i) {
var start = word.slice(0, i);
var end = word.slice(i);
var result = start + c + end;
return result;
}
console.log(getPerms("abc"));
In your code, you loop on words and modify permutation in the same time (so, based on the previous explanation, it is like modifying words and loop on words in the same time), it is this things which create the infinite loop.
I did not check if your algo works, I am just pointing code's problem.
So I think what you want is:
function getPerms(str) {
var permutations = [];
var words = [];
if(str.length == 0) {
permutations.push("");
return permutations;
}
var first = str.charAt(0);//get the first char
var reminder = str.slice(1);//remove the first char
words = getPerms(reminder);
for(var i = 0; i < words.length; i++) {
for(var j = 0; j <= words[i].length; j++) {
var s = insertCharAt(words[i], first, j);
permutations.push(s);
}
}
return permutations;
}
function insertCharAt(word, c, i) {
var start = word.slice(0, i);
var end = word.slice(i);
var result = start + c + end;
return result;
}
console.log(getPerms("abc"));

String with the highest frequency of recurring letters in a word

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;
}

Find the characters in a string which are not duplicated

I have to make a function in JavaScript that removes all duplicated letters in a string. So far I've been able to do this: If I have the word "anaconda" it shows me as a result "anaconda" when it should show "cod". Here is my code:
function find_unique_characters( string ){
var unique='';
for(var i=0; i<string.length; i++){
if(unique.indexOf(string[i])==-1){
unique += string[i];
}
}
return unique;
}
console.log(find_unique_characters('baraban'));
We can also now clean things up using filter method:
function removeDuplicateCharacters(string) {
return string
.split('')
.filter(function(item, pos, self) {
return self.indexOf(item) == pos;
})
.join('');
}
console.log(removeDuplicateCharacters('baraban'));
Working example:
function find_unique_characters(str) {
var unique = '';
for (var i = 0; i < str.length; i++) {
if (str.lastIndexOf(str[i]) == str.indexOf(str[i])) {
unique += str[i];
}
}
return unique;
}
console.log(find_unique_characters('baraban'));
console.log(find_unique_characters('anaconda'));
If you only want to return characters that appear occur once in a string, check if their last occurrence is at the same position as their first occurrence.
Your code was returning all characters in the string at least once, instead of only returning characters that occur no more than once. but obviously you know that already, otherwise there wouldn't be a question ;-)
Just wanted to add my solution for fun:
function removeDoubles(string) {
var mapping = {};
var newString = '';
for (var i = 0; i < string.length; i++) {
if (!(string[i] in mapping)) {
newString += string[i];
mapping[string[i]] = true;
}
}
return newString;
}
With lodash:
_.uniq('baraban').join(''); // returns 'barn'
You can put character as parameter which want to remove as unique like this
function find_unique_characters(str, char){
return [...new Set(str.split(char))].join(char);
}
function find_unique_characters(str, char){
return [...new Set(str.split(char))].join(char);
}
let result = find_unique_characters("aaaha ok yet?", "a");
console.log(result);
//One simple way to remove redundecy of Char in String
var char = "aaavsvvssff"; //Input string
var rst=char.charAt(0);
for(var i=1;i<char.length;i++){
var isExist = rst.search(char.charAt(i));
isExist >=0 ?0:(rst += char.charAt(i) );
}
console.log(JSON.stringify(rst)); //output string : avsf
For strings (in one line)
removeDuplicatesStr = str => [...new Set(str)].join('');
For arrays (in one line)
removeDuplicatesArr = arr => [...new Set(arr)]
Using Set:
removeDuplicates = str => [...new Set(str)].join('');
Thanks to David comment below.
DEMO
function find_unique_characters( string ){
unique=[];
while(string.length>0){
var char = string.charAt(0);
var re = new RegExp(char,"g");
if (string.match(re).length===1) unique.push(char);
string=string.replace(re,"");
}
return unique.join("");
}
console.log(find_unique_characters('baraban')); // rn
console.log(find_unique_characters('anaconda')); //cod
​
var str = 'anaconda'.split('');
var rmDup = str.filter(function(val, i, str){
return str.lastIndexOf(val) === str.indexOf(val);
});
console.log(rmDup); //prints ["c", "o", "d"]
Please verify here: https://jsfiddle.net/jmgy8eg9/1/
Using Set() and destructuring twice is shorter:
const str = 'aaaaaaaabbbbbbbbbbbbbcdeeeeefggggg';
const unique = [...new Set([...str])].join('');
console.log(unique);
Yet another way to remove all letters that appear more than once:
function find_unique_characters( string ) {
var mapping = {};
for(var i = 0; i < string.length; i++) {
var letter = string[i].toString();
mapping[letter] = mapping[letter] + 1 || 1;
}
var unique = '';
for (var letter in mapping) {
if (mapping[letter] === 1)
unique += letter;
}
return unique;
}
Live test case.
Explanation: you loop once over all the characters in the string, mapping each character to the amount of times it occurred in the string. Then you iterate over the items (letters that appeared in the string) and pick only those which appeared only once.
function removeDup(str) {
var arOut = [];
for (var i=0; i < str.length; i++) {
var c = str.charAt(i);
if (c === '_') continue;
if (str.indexOf(c, i+1) === -1) {
arOut.push(c);
}
else {
var rx = new RegExp(c, "g");
str = str.replace(rx, '_');
}
}
return arOut.join('');
}
I have FF/Chrome, on which this works:
var h={};
"anaconda".split("").
map(function(c){h[c] |= 0; h[c]++; return c}).
filter(function(c){return h[c] == 1}).
join("")
Which you can reuse if you write a function like:
function nonRepeaters(s) {
var h={};
return s.split("").
map(function(c){h[c] |= 0; h[c]++; return c}).
filter(function(c){return h[c] == 1}).
join("");
}
For older browsers that lack map, filter etc, I'm guessing that it could be emulated by jQuery or prototype...
This code worked for me on removing duplicate(repeated) characters from a string (even if its words separated by space)
Link: Working Sample JSFiddle
/* This assumes you have trim the string and checked if it empty */
function RemoveDuplicateChars(str) {
var curr_index = 0;
var curr_char;
var strSplit;
var found_first;
while (curr_char != '') {
curr_char = str.charAt(curr_index);
/* Ignore spaces */
if (curr_char == ' ') {
curr_index++;
continue;
}
strSplit = str.split('');
found_first = false;
for (var i=0;i<strSplit.length;i++) {
if(str.charAt(i) == curr_char && !found_first)
found_first = true;
else if (str.charAt(i) == curr_char && found_first) {
/* Remove it from the string */
str = setCharAt(str,i,'');
}
}
curr_index++;
}
return str;
}
function setCharAt(str,index,chr) {
if(index > str.length-1) return str;
return str.substr(0,index) + chr + str.substr(index+1);
}
Here's what I used - haven't tested it for spaces or special characters, but should work fine for pure strings:
function uniquereduce(instring){
outstring = ''
instringarray = instring.split('')
used = {}
for (var i = 0; i < instringarray.length; i++) {
if(!used[instringarray[i]]){
used[instringarray[i]] = true
outstring += instringarray[i]
}
}
return outstring
}
Just came across a similar issue (finding the duplicates). Essentially, use a hash to keep track of the character occurrence counts, and build a new string with the "one-hit wonders":
function oneHitWonders(input) {
var a = input.split('');
var l = a.length;
var i = 0;
var h = {};
var r = "";
while (i < l) {
h[a[i]] = (h[a[i]] || 0) + 1;
i += 1;
}
for (var c in h) {
if (h[c] === 1) {
r += c;
}
}
return r;
}
Usage:
var a = "anaconda";
var b = oneHitWonders(a); // b === "cod"
Try this code, it works :)
var str="anaconda";
Array.prototype.map.call(str,
(obj,i)=>{
if(str.indexOf(obj,i+1)==-1 && str.lastIndexOf(obj,i-1)==-1){
return obj;
}
}
).join("");
//output: "cod"
This should work using Regex ;
NOTE: Actually, i dont know how this regex works ,but i knew its 'shorthand' ,
so,i would have Explain to you better about meaning of this /(.+)(?=.*?\1)/g;.
this regex only return to me the duplicated character in an array ,so i looped through it to got the length of the repeated characters .but this does not work for a special characters like "#" "_" "-", but its give you expected result ; including those special characters if any
function removeDuplicates(str){
var REPEATED_CHARS_REGEX = /(.+)(?=.*?\1)/g;
var res = str.match(REPEATED_CHARS_REGEX);
var word = res.slice(0,1);
var raw = res.slice(1);
var together = new String (word+raw);
var fer = together.toString();
var length = fer.length;
// my sorted duplicate;
var result = '';
for(var i = 0; i < str.length; i++) {
if(result.indexOf(str[i]) < 0) {
result += str[i];
}
}
return {uniques: result,duplicates: length};
} removeDuplicates('anaconda')
The regular expression /([a-zA-Z])\1+$/ is looking for:
([a-zA-Z]]) - A letter which it captures in the first group; then
\1+ - immediately following it one or more copies of that letter; then
$ - the end of the string.
Changing it to /([a-zA-Z]).*?\1/ instead searches for:
([a-zA-Z]) - A letter which it captures in the first group; then
.*? - zero or more characters (the ? denotes as few as possible); until
\1 - it finds a repeat of the first matched character.
I have 3 loopless, one-line approaches to this.
Approach 1 - removes duplicates, and preserves original character order:
var str = "anaconda";
var newstr = str.replace(new RegExp("[^"+str.split("").sort().join("").replace(/(.)\1+/g, "").replace(/[.?*+^$[\]\\(){}|-]/g, "\\$&")+"]","g"),"");
//cod
Approach 2 - removes duplicates but does NOT preserve character order, but may be faster than Approach 1 because it uses less Regular Expressions:
var str = "anaconda";
var newstr = str.split("").sort().join("").replace(/(.)\1+/g, "");
//cdo
Approach 3 - removes duplicates, but keeps the unique values (also does not preserve character order):
var str = "anaconda";
var newstr = str.split("").sort().join("").replace(/(.)(?=.*\1)/g, "");
//acdno
function removeduplicate(str) {
let map = new Map();
// n
for (let i = 0; i < str.length; i++) {
if (map.has(str[i])) {
map.set(str[i], map.get(str[i]) + 1);
} else {
map.set(str[i], 1);
}
}
let res = '';
for (let i = 0; i < str.length; i++) {
if (map.get(str[i]) === 1) {
res += str[i];
}
}
// o (2n) - > O(n)
// space o(n)
return res;
}
If you want your function to just return you a unique set of characters in your argument, this piece of code might come in handy.
Here, you can also check for non-unique values which are being recorded in 'nonUnique' titled array:
function remDups(str){
if(!str.length)
return '';
var obj = {};
var unique = [];
var notUnique = [];
for(var i = 0; i < str.length; i++){
obj[str[i]] = (obj[str[i]] || 0) + 1;
}
Object.keys(obj).filter(function(el,ind){
if(obj[el] === 1){
unique+=el;
}
else if(obj[el] > 1){
notUnique+=el;
}
});
return unique;
}
console.log(remDups('anaconda')); //prints 'cod'
If you want to return the set of characters with their just one-time occurrences in the passed string, following piece of code might come in handy:
function remDups(str){
if(!str.length)
return '';
var s = str.split('');
var obj = {};
for(var i = 0; i < s.length; i++){
obj[s[i]] = (obj[s[i]] || 0) + 1;
}
return Object.keys(obj).join('');
}
console.log(remDups('anaconda')); //prints 'ancod'
function removeDuplicates(str) {
var result = "";
var freq = {};
for(i=0;i<str.length;i++){
let char = str[i];
if(freq[char]) {
freq[char]++;
} else {
freq[char] =1
result +=char;
}
}
return result;
}
console.log(("anaconda").split('').sort().join('').replace(/(.)\1+/g, ""));
By this, you can do it in one line.
output: 'cdo'
function removeDuplicates(string){
return string.split('').filter((item, pos, self)=> self.indexOf(item) == pos).join('');
}
the filter will remove all characters has seen before using the index of item and position of the current element
Method 1 : one Simple way with just includes JS- function
var data = 'sssssddddddddddfffffff';
var ary = [];
var item = '';
for (const index in data) {
if (!ary.includes(data[index])) {
ary[index] = data[index];
item += data[index];
}
}
console.log(item);
Method 2 : Yes we can make this possible without using JavaScript function :
var name = 'sssssddddddddddfffffff';
let i = 0;
let newarry = [];
for (let singlestr of name) {
newarry[i] = singlestr;
i++;
}
// now we have new Array and length of string
length = i;
function getLocation(recArray, item, arrayLength) {
firstLaction = -1;
for (let i = 0; i < arrayLength; i++) {
if (recArray[i] === item) {
firstLaction = i;
break;
}
}
return firstLaction;
}
let finalString = '';
for (let b = 0; b < length; b++) {
const result = getLocation(newarry, newarry[b], length);
if (result === b) {
finalString += newarry[b];
}
}
console.log(finalString); // sdf
// Try this way
const str = 'anaconda';
const printUniqueChar = str => {
const strArr = str.split("");
const uniqueArray = strArr.filter(el => {
return strArr.indexOf(el) === strArr.lastIndexOf(el);
});
return uniqueArray.join("");
};
console.log(printUniqueChar(str)); // output-> cod
function RemDuplchar(str)
{
var index={},uniq='',i=0;
while(i<str.length)
{
if (!index[str[i]])
{
index[str[i]]=true;
uniq=uniq+str[i];
}
i++;
}
return uniq;
}
We can remove the duplicate or similar elements in string using for loop and extracting string methods like slice, substring, substr
Example if you want to remove duplicate elements such as aababbafabbb:
var data = document.getElementById("id").value
for(var i = 0; i < data.length; i++)
{
for(var j = i + 1; j < data.length; j++)
{
if(data.charAt(i)==data.charAt(j))
{
data = data.substring(0, j) + data.substring(j + 1);
j = j - 1;
console.log(data);
}
}
}
Please let me know if you want some additional information.

What's the most efficient algorithm to find the indexes of multiple characters inside a string (javascript)?

I'm looking for the fastest method I can use to search a body of text for the indexes of multiple characters.
For example:
searchString = 'abcdefabcdef';
searchChars = ['a','b'];
// returns {'a':[0,6], 'b':[1,7]}
You should be able to use a regular expression to find all occurances of each character. Something like:
function findIndexes(find, str) {
var output = {};
for (var i = 0; i < find.length; i++) {
var m = [];
var r = new RegExp('.*?' + find[i], 'g');
var ofs = -1;
while ((x = r.exec(str)) != null) {
ofs += x[0].length;
m.push(ofs);
}
output[find[i]] = m;
}
return output;
}
Edit:
Did some changes, and now it works. :) However, as Javascript doesn't have a matches method to get all matches at once, it's not really any improvment over using indexOf... :P
Edit 2:
However, you can use a regular expression to find any of the characters, so you only need to loop the string once instead of once for each character. :)
function findIndexes(find, str) {
var output = {};
for (var i = 0; i < find.length; i++) output[find[i]] = [];
var r = new RegExp('.*?[' + find.join('') + ']', 'g');
var ofs = -1;
while ((x = r.exec(str)) != null) {
ofs += x[0].length;
output[x[0].substr(x[0].length-1,1)].push(ofs);
}
return output;
}
Assuming few letters to search for and many letters to search against (i.e. low number of letter, long strings), the latter is the most efficient, since you only go through the string once, and then test each letter.
The other one goes through the string as many times as there are letters to search for.
After timing a few single pass algorithms and Guffa's regex, I ended up going with this:
function findIndexesMultiPass(str,find) {
var x, output = {};
for (var i = 0; i < find.length; i++) {
output[find[i]] = [];
x = 0;
while ((x = str.indexOf(find[i], x)) > -1) {
output[find[i]].push(x++);
}
}
return output;
}
var searchString = "abcd abcd abcd";
var searchChars = ['a', 'b'];
var result = findIndexesMultiPass(searchString, searchChars);
// {'a':[0,5,10], 'b':[1,6,11]}
This turned out to be pretty slow:
function findIndexesOnePass(str,find) {
var output = {};
for (var i = 0; i < find.length; i++) {
output[find[i]] = [];
}
for (var i = 0; i < str.length; i++) {
var currentChar = str.charAt(i);
if (output[currentChar] !== undefined) {
output[currentChar].push(i);
}
}
return output;
}
var searchString = "abcd abcd abcd";
var searchChars = ['a', 'b'];
var result = findIndexesOnePass(searchString, searchChars);
// {'a':[0,5,10], 'b':[1,6,11]}
Rough times (indexes of 3 characters)
Google Chrome (Mac)
findIndexesMultiPass: 44ms
findIndexesOnePass: 799ms
findIndexesRegEx: 95ms
Safari
findIndexesMultiPass: 48ms
findIndexesOnePass: 325ms
findIndexesRegEx: 293ms
Firefox
findIndexesMultiPass: 56ms
findIndexesOnePass: 369ms
findIndexesRegEx: 786ms

Categories