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.
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))
I want to write a function such as:
function extract(template, example){
//some magic
return obj;
}
That given an input like the following:
var template = "/some/path/{param_one}/{another_param}/etc?arg1={value}";
var example = "/some/path/foo/bar/etc?arg1=baz";
Would return this object:
{param_one: "foo", another_param: "bar", value: "baz"}
I don't have control over the template nor the example (e.g. I can't change the template to be a regex with named capture groups). What I can safely assume is that the given example will match the template.
As a start, I was able to extract the keys with this:
var re = /\{(.+?)\}/g;
var match;
do {
match = re.exec(template)
if (match) {
console.log(match[1]);
}
} while (match);
I use regex and replace to solve:
function extract(template, example){
//some magic
var re = /(?:([^\{]*)\{([^\}]*)\})/g;
var result = example;
var params = [];
var match, values, obj = {};
do {
match = re.exec(template)
if (match) {
result = result.replace(match[1], '|');
params.push(match[2]);
}
} while (match);
values = result.split('|');
for(var i=0;i < params.length; i++) {
obj[params[i]] = values[i+1];
}
return obj;
}
First, it get params part and text part use regex, then it replace all text part with | and store key to params array.
Finally, it loops through params array and match value splited from above
Hope this help
I would break this into two parts - build a template object, then use that template object to parse your string(s). The following function buildTemplate(pattern) returns a template object. You can then use this object to parse as many strings as you need, for example buildTemplate(pattern).parse(string) or even var parser = buildTemplate(pattern); var parameters = parser.parse(string);.
function buildTemplate(pattern) {
return (function(pattern) {
function splitString(string) {
return string.split(/[/?&]+/);
}
var glob = {};
glob.parts = splitString(pattern).map(function(part) {
var trimmedPart = part.replace(/^\{|\}$/g, '');
var isLiteral = part.length !== trimmedPart.length + 2;
return {
isLiteral: isLiteral,
value: isLiteral ? part : trimmedPart:
};
});
glob.parse = function(string) {
var stringParts = splitString(string);
if (stringParts.length !== glob.parts.length) {
return null;
}
var params = {};
for (var i = 0; i < stringParts.length; i++) {
if (blob.parts[i].isLiteral) {
if (blob.parts[i].value !== stringParts[i]) {
return null;
}
} else {
params[blob.parts[i].value] = stringParts[i];
}
}
return params;
}
return glob;
})(pattern);
}
Note: This code is completely untested. Let me know if something doesn't work as intended.
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'm receiving a String like this:
"45,21,555,64,94,796,488,\n " the \n means new line
is there a way to cut the string based on "," and getting only the "number".
I could do it C but how can I search for a character in JavaScript.
thanks for any hint
var parts = "45,21,555,64,94,796,488,\n ".split(',').filter(function(val) {
var num = parseInt(val, 10);
return !isNaN(num) && toString.call(num) === '[object Number]';
});
// parts: ["45", "21", "555", "64", "94", "796", "488"]
This is taking your String and splitting it into an Array based on a delimiter (',') and then running it through a filter function to remove anything that does not evaluate to a valid Number.
See String.prototype.split and Array.prototype.filter.
If you actually want to then convert those values to Numbers, you could chain a map call:
var parts = "45,21,555,64,94,796,488,\n ".split(',')
.filter(function(val) {
var num = parseInt(val, 10);
return !isNaN(num) && toString.call(num) === '[object Number]';
})
.map(function(val) {
return parseInt(val, 10);
});
// parts: [45, 21, 555, 64, 94, 796, 488]
Yes, like this:
var myString = "45,21,555,64,94,796,488,\n ";
var splitStrings = string.split(",");
console.log(splitStrings); //Should log an array to the console, containing only your strings e.g. [45,21,555,64,94,796,488,\n]
This returns an array of strings, split by the character you passed in. You can read more on this method here: http://www.w3schools.com/jsref/jsref_split.asp
After that, you can parse your array to remove anything you don't want like the new line character, or use a filter method to do it inline as detailed in another answer
You can use string.split(splitChar) to split. Then you can use .map or .filter to convert items to number.
var str = "45,21,555,64,94,796,488,\n ";
var arr = str.split(",");
document.write("Array: <pre>"+JSON.stringify(arr)+"</pre>");
var nums = arr.map(function(item){
return parseInt(item);
});
document.write("Numbers: <pre>"+JSON.stringify(nums)+"</pre>");
If you are wanting just the numbers as an array you could do this.
var str = "45,21,555,64,94,796,488,\n ";
var arr = str.split(","); //Make the string into an array
var len = arr.length;
for(var i=0;i<len;i++)
{
try
{
arr[i] = parseInt(arr[i]); // convert the numbers to ints
}
catch(e)
{
arr[i] = null;
}
}
As the fact that strings are just arrays, you can do it this way (a bit more comprehensive, in my opinion):
function noCommas(a)
{
var b = '';
for (var i = 0; i < a.length; i++)
{
if (a[i] != ',') //&& a[i]!= String.fromCharCode(10)) If you want no new lines too
{
b += a[i];
} else
{
//break; -> In case you only want the first value
}
}
return parseInt(b); //To return the value as an integer
}
noCommas('1234,6789'); // returns 12346789
I missunderstood the question, so here's my fixed code:
function noCommas(a)
{
var b = [];
var tempNum = '';
for (var i = 0; i < a.length; i++)
{
if (a[i] != ',' && i != a.length - 1)
{
tempNum += a[i];
} else
{
b.push(parseInt(tempNum))
tempNum = '';
}
}
return b;
}
now if you do noCommas('123,345,324234')[2] it would return 324234.
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};
});