I am sorry for my english.
var r=/([a-z]*)[a]{1}([a-z]*)/igm;
var s="araba";
The result I need,
a raba index:0
ar a ba index:2
arab a index:4
How can I do that with regexp ?
A single regular expression call will not be possible as regular expressions can't go back after they match something. You'll have to make a regex that will look for the character set you want (in your example [a]) and stop at every match, generating a new result and pushing it into an array (or use it directly). RegExp.prototype.exec is needed like this:
function mySplit(str, charList) {
// regex will match any character provided in the charList string (no need for {1} as it is the default)
var regex = new RegExp("[" + charList + "]", "g");
// the result array, will contain object indecating where the match was found and the parts
var result = [];
// before starting, execute the regex on the string str
regex.exec(str);
// using do-while to guarantee that there will be at least one result in the result array
do {
// the index of this match
var index = regex.lastIndex - 1;
// the result object for this match
var r = {
index: index, // the index
parts: [] // the parts (for example "a", "raba" ...)
};
var p;
// PREFIX PART
p = str.substr(0, index); // get the prefix
if(p.length) r.parts.push(p); // if not empty push it
// THE CHARACTER
p = str.substr(index, 1); // get it
if(p.length) r.parts.push(p); // if not empty push it (this is could be empty if nothing is matched)
// POSTFIX PART
p = str.substr(index + 1); // get it
if(p.length) r.parts.push(p); // push it if not empty
result.push(r); // push the object r as a result
} while(regex.exec(str));
return result;
}
console.log(mySplit("araba", "a"));
Note: the second parametter to mySplit could as much letters as you want. For example mySplit("araba", "ab"); will return this:
[
{
"index": 0,
"parts": [
"a",
"raba"
]
},
{
"index": 2,
"parts": [
"ar",
"a",
"ba"
]
},
{
"index": 3,
"parts": [
"ara",
"b",
"a"
]
},
{
"index": 4,
"parts": [
"arab",
"a"
]
}
]
Thank you İbrahim :)
My extended solution :
String.prototype.matchAllCombinations=function(regex,includeIndex,constGroups){
var str=this;
var includeIndex=includeIndex||false;
var constGroups=constGroups||[];
var newRegex;
if(constGroups.length==0){
var groups=regex.source.split(new RegExp('([(]{1}[^()]+[)]{1})','g'));
var newSource='';
for(var el,it=1,lim=groups.length-1;it<lim;it++){
el=groups[it];
if(el.charAt(0)!='('){
newSource+='('+el+')';
constGroups.push(it-1);
}else{
newSource+=el;
}
}
newRegex=new RegExp(newSource,'');
}else{
newRegex=regex;
}
var testStr=str;
var combinations=new Array();
var matches=new Array();
var combination=new Array();
var end=new String();
var k=new Number();
var constValues=new Array();
var itCount=new Number();
var lastTestStr=new String();
while((matches=testStr.match(newRegex))!=null){
k=2;
correct=true;
combination=new Array();
combination=matches.slice(1,matches.length);
if(combination[combination.length-1]!=""){
k=1;
}
combination[combination.length-k]+=end;
end=combination[combination.length-k];
if(itCount==0){
for(var it=0;it<constGroups.length;it++){
constValues.push(combination[constGroups[it]]);
}
}
testStr=combination.slice(0,combination.length-k).join('');
if(lastTestStr==testStr){
combinations=[];
break;
}
for(var it=0;it<constGroups.length;it++){
correct&=(combination[constGroups[it]].length==constValues[it].length);
}
if(correct){
if(includeIndex){
combination["indexes"]=new Array();
for(var it=0;it<constGroups.length;it++){
combination["indexes"].push(combination.slice(0,constGroups[it]).join('').length);
}
}
combinations.push(combination);
}
lastTestStr=testStr;
itCount++;
}
return combinations;
}
console.log('araba'.matchAllCombinations(new RegExp('([a-z]*)[a]{1}([a-z]*)'),true));
console.log('araba'.matchAllCombinations(new RegExp('([a-z]*)[a]{1}([a-z]*)[a]{1}([a-z]*)'),true));
console.log('araba'.matchAllCombinations(new RegExp('([a-z]*)[a]{1}([a-z]*)[a]{1}([a-z]*)[a]{1}([a-z]*)'),true));
console.log('araba'.matchAllCombinations(new RegExp('([a-z]*)[a]{1}([a-z]*)[a]{1}([a-z]*)[a]{1}([a-z]*)[a]{1}([a-z]*)'),true));
console.log('araba'.matchAllCombinations(new RegExp('([a-z]*)[p]{1}([a-z]*)'),true));
console.log('erebe'.matchAllCombinations(new RegExp('([a-z]*)([a]{1}|[e]{1})([a-z]*)'),true,[1]));
Related
I am trying to find the places of each letter in a sentence by using "dictionaries". The problem is I want to find all the places that each letter is and not only the last one. I am very new to JavaScript and couldn't figure out the way to do it.
function letters(stringArgument) {
stringArgument = stringArgument.replace(/ /g,'');
var dict = {};
for (var i=0; i < stringArgument.length; i++ )
if (!stringArgument[i] in dict){
dict[stringArgument[i]] = [];
}else{
dict[stringArgument[i]] = [i+1]
}
return dict
}
var a = letters('Lost time is never found again.');
console.log(a);
naturally gives this output:
{ L: [ 1 ], o: [ 17 ], s: [ 10 ], t: [ 5 ]...
but it should give this:
{ L: [ 1 ], o: [ 2, 17 ], s: [ 3, 10 ], t: [ 4, 5 ]...
Also each letter is saved to the dictionary at the same order they appear in the sentence, how can I order the letters alphabetically?
What you need is a function that gets the positions of a character in a given string.
Try this:
function findAllPositions(char, content) {
var result = [];
let index = content.indexOf(char);
while(index !== -1) {
result.push(index);
index = content.indexOf(char, index + 1);
}
return result;
}
findAllPositions('o', 'Lost time is never found again.'); // Result = [1, 20]
Using this we can update the letter function as follows:
function letters(stringArgument) {
stringArgument = stringArgument.replace(/ /g, '');
var dict = {};
for (const char of stringArgument) {
dict[char] = findAllPositions(char, stringArgument)
}
return dict;
}
letters('is again.')
/*
{
"i": [0, 5],
"s": [1],
"a": [2, 4],
"g": [3],
"n": [6],
".": [7]
}
*/
You need to have
parantheses for the check
if (!(stringArgument[i] in dict)) {
create an array if the above is true
push the postion to the array
For getting a sorted output, you could take the entries of the object, apply a sorting by taking the key and show the result in order.
Object have an insertation oder for not positive 32 bit numbers (like indixes) or symbols. The index like numbers are sorted by value and appears first in the object.
function letters(stringArgument) {
stringArgument = stringArgument.replace(/ /g, '');
var dict = {};
for (var i = 0; i < stringArgument.length; i++) {
if (!(stringArgument[i] in dict)) {
dict[stringArgument[i]] = [];
}
dict[stringArgument[i]].push(i + 1);
}
return dict;
}
var a = letters('Lost time is never found again.');
Object
.entries(a)
.sort(([a], [b]) => a.localeCompare(b))
.forEach(([key, positions]) => console.log(key, ...positions));
console.log(a);
First, for any item, if it is not in an empty array:
var notInDict = !(stringArgument[i] in dict);
If not in dict, then initialize an empty array and push the item in it using
dict[stringArgument[i]].push(i + 1);
Try this.
function letters(stringArgument) {
stringArgument = stringArgument.replace(/ /g, "");
var dict = {};
for (var i = 0; i < stringArgument.length; i++) {
var notInDict = !(stringArgument[i] in dict);
if (notInDict) {
dict[stringArgument[i]] = [];
}
dict[stringArgument[i]].push(i + 1);
}
return dict;
}
var a = letters("Lost time is never found again.");
console.log(a);
you are assigning a new array at each iteration
dict[stringArgument[i]] = [i+1]
what you need to do is push the new position to existing array.
dict[stringArgument[i]].push(i+1)
also, remove the else block
function letters(stringArgument) {
stringArgument = stringArgument.toLowerCase().replace(/ /g,'');
var dict = {};
for (var i=0; i < stringArgument.length; i++ ){
if (!dict.hasOwnProperty(stringArgument[i])){
dict[stringArgument[i]] = [];
}
dict[stringArgument[i]].push(i+1);
}
//sorting
var letters = Object.keys(dict); //returns a array
letters.sort();
var sortedDic = {};
for(var i in letters) {
sortedDic[letters[i]] = dict[letters[i]];
}
return sortedDic;
}
var a = letters('Lost time is never found again.');
console.log(a);
for the first part you can also do that:
let sentence = 'Lost time is never found again.'
let tabLetters = [...sentence.replace(/ /g,'')].reduce((a,c,i)=>
{
if (!a[c]) a[c] = [i+1]
else a[c].push(i+1)
return a
},{})
document.write(JSON.stringify(tabLetters))
Given an array of words, write a function that returns an array of the words that occur an even number of times.
function even(["hello", "hi", "hello", "elephant", "hi"]);
That output should be:
["hello", "hi"]
This has been a toy problem I have been struggling with recently. I have solved similar problems counting and returning the number of occurrences of elements in an array but am having trouble taking that logic and applying it to this problem.
This is what I have tried so far, but have hit a wall when trying to output just the even occurrences:
function even(collection) {
var results = [];
for(var i = 0; i < collection.length; i++){
var value = collection[i];
if(results[value]){
results[value] = results[value] + 1;
}else{
results[value] = 1;
}
}
return results;
}
You can use reduce to get an actual count of the words, then simply return an array of the ones that have an even count:
function even(wordsArr) {
//Object of words and counts
var wordCounts = wordsArr.reduce(function(counts, word) {
if (!counts.hasOwnProperty(word)) {
counts[word] = 0;
}
counts[word]++;
return counts;
}, {});
//Now filter that out and return
return Object.keys(wordCounts).filter(function(word) {
return wordCounts[word] % 2 === 0
});
}
even(["hello", "hi", "hello", "elephant", "hi"]); //["hello", "hi"]
var arr = ["hello", "hi", "hello", "elephant", "hi"];
function onlyEvens( arr )
{
var countObj = {};
for( var i = 0; i < arr.length; i++ )
{
var item = arr[i];
if( countObj[ item ] !== undefined )
countObj[item]++;
else
countObj[item] = 1;
}//for()
var filteredArray = [];
for(var key in countObj )
{
if( countObj[key] % 2 == 0 )
filteredArray.push( key );
}
return filteredArray;
}//onlyEvens()
console.log( onlyEvens( arr ) );
Issues in your code:
you use collection instead of words
you cannot access array the associative way. You must declare it as object:
results[value]
you return result variable, but it is undeclared.
return result;
results only contains the occurrences of every word. There miss the code that calculates if the occurrences of a word are odd or even.
fixed code:
function even(words) { // <<< in your code was collection
var results = {};
for(var i = 0; i < words.length; i++){
var value = words[i];
if(results[value]){
results[value] = results[value] + 1;
}else{
results[value] = 1;
}
}
var ret = [];
for(var word in results)
if(results[word]%2 !== 0)
rest.push(word);
return ret;
}
function even(list) {
var d = list.reduce(function(d, w) { d[w] = !d[w]; return d; }, {});
return Object.keys(d).filter(function(w) { return !d[w]; });
}
console.log(even(["hello", "hi", "hello", "elephant", "hi"]));
console.log(even(["hello", "yo", "yo", "hi", "hello", "yo", "elephant", "hi"]));
Explanation: Use the array .reduce() method to create an object (d) with a property for each word (w) with a boolean value indicating whether the word has an odd number of occurrences. Then .filter() the keys to get all the ones that are not odd.
If you previously sort the array you can filter it as required in just a code line like this :
var even = (str) => str.sort().filter((element, index, arr) => index+1 === arr.lastIndexOf(element));
console.log(even(["hello", "hello", "hi", "elephant", "hi", "hi"])); //[ 'hello', 'hi' ]
I am trying to come up with a function that will take any string and output an array of numbers and strings without using .split(). Below are the tests it needs to pass and a function that passes the tests currently. I am curious about how other people would solve this.
function csvParse(inputString) {
var outputArray = [];
var inputArray = inputString.split(',');
for (var i =0; i < inputArray.length; i++) {
if (!Number.isNaN(+inputArray[i])) {
outputArray.push(+inputArray[i]);
} else {
outputArray.push(inputArray[i].replace(/['"]+/g,'').trim());
}
}
return outputArray;
};
describe('CSV Parse', function() {
it('should parse a string of integers correctly', function() {
var input = '3,7,9,1,25';
var output = [ 3, 7, 9, 1, 25 ];
expect(csvParse(input)).to.deep.equal(output);
});
it('should parse a string of strings correctly', function() {
var input = '"3","7","9","1","25"';
var output = ["3", "7", "9", "1", "25"];
expect(csvParse(input)).to.deep.equal(output);
});
it('should parse a string of integers and strings correctly', function() {
var input = '1, "one", 2, "two", 3, "three"';
var output = [1, "one", 2, "two", 3, "three"];
expect(csvParse(input)).to.deep.equal(output);
});
});
Basic JS solution just replacing the split method as you asked (fiddle here)
function dumbComaSplit(inputString) {
var strArray = [];
var tmpStr = "";
for (var i = 0; i < inputString.length; i++) {
if (inputString.charAt(i) == ',') {
strArray.push(tmpStr);
tmpStr = "";
continue;
}
tmpStr += inputString.charAt(i);
}
strArray.push(tmpStr);
return strArray;
};
function csvParse(inputString) {
var outputArray = [];
var inputArray = dumbComaSplit(inputString);
for (var i =0; i < inputArray.length; i++) {
if (!Number.isNaN(+inputArray[i])) {
outputArray.push(+inputArray[i]);
} else {
outputArray.push(inputArray[i].replace(/['"]+/g,'').trim());
}
}
return outputArray;
};
If you really really want to parse CSV format without spilt, here is one way you can do it using new ECMAScript 6 Template Strings feature.
The basic is that we replace CSV , with mock ${b} expressions and convert the string to a Template String. Then evaluate it while using tag feature. This will create the array for us.
Here is the fiddle.
function csvParse(inputString) {
b = null; //dummy variable
//Prepare the notation to be a TemplateString. Use var b in a dummy expression
inputString = "`" + inputString.replace(/,/g, '${b}') + "`";
//Evaluate. Note that we use a tag function inside the evaluation to create the array
//We also pass the string as a Template String to evaluate. This is optional
fn = new Function(`function tag(inputArray) {
return inputArray;
}
return tag ${inputString} `);
//Return the array
return fn().raw;
};
console.log(csvParse("power,of,es6"));
split is the way to go. But this is the answer you are looking for.
I want to get length of every element in array
my code is
var a = "Hello world" ;
var chars = a.split(' ');
so I will have an array of
chars = ['Hello' , 'world'] ;
but how I can get length of each word like this ?
Hello = 5
world = 5
You can use map Array function:
var lengths = chars.map(function(word){
return word.length
})
ES6 is now widely available (2019-10-03) so for completeness — you can use the arrow operator with .map()
var words = [ "Hello", "World", "I", "am", "here" ];
words.map(w => w.length);
> Array [ 5, 5, 1, 2, 4 ]
or, very succinctly
"Hello World I am here".split(' ').map(w => w.length)
> Array [ 5, 5, 1, 2, 4 ]
The key here is to use .length property of a string:
for (var i=0;i<chars.length;i++){
console.log(chars[i].length);
}
You could create a results object (so you have the key, "hello", and the length, 5):
function getLengthOfWords(str) {
var results = {};
var chars = str.split(' ');
chars.forEach(function(item) {
results[item] = item.length;
});
return results;
}
getLengthOfWords("Hello world"); // {'hello': 5, 'world': 5}
Try map()
var words = ['Hello', 'world'];
var lengths = words.map(function(word) {
return word + ' = ' + word.length;
});
console.log(lengths);
You can use forEach, if you want to keep the words, and the length you can do it like this:
var a = "Hello world" ;
var chars = a.split(' ');
var words = [];
chars.forEach(function(str) {
words.push([str, str.length]);
});
You can then access both the size and the word in the array.
Optionally you could have a little POJO object, for easier access:
var a = "Hello world" ;
var chars = a.split(' ');
var words = [];
chars.forEach(function(str) {
words.push({word: str, length: str.length});
});
Then you can access them like:
console.log(words[0].length); //5
console.log(words[0].word); //"Hello"
Or using map to get the same POJO:
var words = chars.map(function(str) {
return {word: str, length: str.length};
});
I would like to get the full string element from an array that matches multiple substrings in no particular order. If more than one match, an exception should be thrown.
var thisArray = [ '/something_else/', '/and_something_else/', '/and_something_else_here/'];
var thisMatch = [ 'some', 'and', 'here'];
var matchingElement = new RegExp(thisArray , thisMatch); // Not sure about this
What I want is for matchineElement to contain the string "and_something_else_here" after the regular expression has executed.
You could do something like this:
var thisArray = [ '/something_else/', '/and_something_else/', '/and_something_else_here/'];
var thisMatch = [ 'some', 'and', 'here'];
function matchElements(arr, arrMatch) {
var tmpArr;
arrMatch.forEach(function(el, i, a) {
tmpArr = arr.filter(function(str) {
return str.indexOf(el) !== -1;
});
});
if (tmpArr.length > 1) {
throw ("Hey, too many matches");
} else {
return tmpArr[0];
}
}
console.log(matchElements(thisArray, thisMatch));
JSFiddle: http://jsfiddle.net/Le53y7ee/
Explanation:
The function goes through every element in the array containing substrings and filters main array keeping only strings that match. After the loop is done the array should contain only string(s) that matched all required substrings.
A regexp that matches a string containing all of a set of substrings looks like:
^(?=.*?string1)(?=.*?string2)(?=.*?string3).*$
So you just need to convert your thisMatch array into such a regular expression.
var regexp = new RegExp('^' + thisMatch.map(function(x) {
return '(?=.*?' + x + ')';
}).join('') + '.*$');
Then you can find all the matches with:
var matchingElements = thisArray.filter(function(x) {
return regexp.test(x);
});
if (matchingElements.length != 1) {
throw new Error('Single match not found');
} else {
matchingElement = matchingElements[0];
}
DEMO
Try
var matchingElement = thisArray.filter(function(val, key) {
var value = val.split(/[^a-z]/).filter(String).join(" ");
// if `.test()` returns `true` three times at `value`
return new RegExp(this[0]).test(value)
&& new RegExp(this[1]).test(value)
&& new RegExp(this[2]).test(value)
? val
: null
}.bind(thisMatch))[0].replace(/\//g,"");)
var thisArray = [ '/something_else/', '/and_something_else/', '/and_something_else_here/'];
var thisMatch = [ 'some', 'and', 'here'];
var matchingElement = thisArray.filter(function(val, key) {
var value = val.split(/[^a-z]/).filter(String).join(" ");
// if `.test()` returns `true` three times at `value`
return new RegExp(this[0]).test(value)
&& new RegExp(this[1]).test(value)
&& new RegExp(this[2]).test(value)
? val
: null
}.bind(thisMatch))[0].replace(/\//g,"");
document.write(matchingElement)
Take those slashes off of both sides of your Strings in those Arrays. Then use the arrayMatch function below, and loop over your indexes to get the results of thisArray, like:
function arrayMatch(yourArray, wordMatchArray){
var ix = [], c = wordMatchArray.length;
for(var i=0,l=yourArray.length; i<l; i++){
var m = 0;
for(var n=0; n<c; n++){
if(yourArray[i].match(new RegExp(wordMatchArray[n])))m++;
if(c === m)ix.push(i);
}
}
if(!ix[0]){
return false;
}
return ix;
}
var indexes = arrayMatch(thisArray, thisMatch);
for(var i=0,l=indexes.length; i<l; i++){
var getMatch = thisArray[indexes[i]];
}