Related
I am currently doing a codewars problem, and I think I almost got it however, I ran across a problem when sorting index values with the same letter. link to problem is here. https://www.codewars.com/kata/5782dd86202c0e43410001f6
function doMath(s) {
let strSplit = s.split(' ');
let clonedArr = strSplit.slice();
for (let i = 0; i < strSplit.length; i++) {
for (let j = 0; j < strSplit[i].length; j++) {
let current = strSplit[i][j];
if (isNaN(current)) {
let letter = current;
strSplit[i] = strSplit[i].replace(letter, '');
strSplit[i] = letter + strSplit[i];
}
}
}
let sortedArr = strSplit.sort();
console.log(sortedArr);
// ["b900", "y369", "z123", "z246", "z89"]
let noLetterArr = sortedArr.map(x => {
return x.slice(1);
});
let numberArr = noLetterArr.map(y => {
return +y;
})
let firstEl = numberArr[0];
for (let i = 1; i < numberArr.length; i++) {
if (numberArr.indexOf(numberArr[i]) % 4 == 1) {
firstEl += numberArr[i];
}
if (numberArr.indexOf(numberArr[i]) % 4 == 2) {
firstEl -= numberArr[i];
}
if (numberArr.indexOf(numberArr[i]) % 4 == 3) {
firstEl *= numberArr[i];
}
}
return firstEl;
}
console.log(doMath('24z6 1z23 y369 89z 900b'));
I would like to sort the sortedArr the ones with the same letter by how they first appeared in string. So since "z246" appeared first in the original string. I would like to have that before "1z23". I had a hard time creating a function for that.
var al = [];
function doMath(s) {
var ar = s.split(" ");
for (let i = 0; i < ar.length; i++) {
for (let char of ar[i]) {
let temp = char.match(/[a-z]/i);
if (temp) {
al[i] = char;
ar[i] = ar[i].replace(char, '');
ar[i] = char + ar[i];
}
}
}
al = al.sort();
//New Sort Logic to pass above test case and others too
var n = [];
for (let i = 0; i < al.length; i++) {
for (let j = 0; j < ar.length; j++) {
if (ar[j].startsWith(al[i]) && !n.includes(ar[j])) {
n.push(ar[j]);
}
}
}
var result = parseInt(n[0].substr(1)),
count = 1;
for (let i = 1; i < n.length; i++) {
if (count == 1) {
result = result + parseInt(n[i].substr(1));
count++;
} else if (count == 2) {
result = result - parseInt(n[i].substr(1));
count++;
} else if (count == 3) {
result = result * parseInt(n[i].substr(1));
count++;
} else if (count == 4) {
result = result / parseInt(n[i].substr(1));
count = 1;
}
}
return Math.round(result);
}
The challenge is next:
At a job interview, you are challenged to write an algorithm to check if a given string, s, can be formed from two other strings, part1 and part2.
The restriction is that the characters in part1 and part2 are in the same order as in s.
The interviewer gives you the following example and tells you to figure out the rest from the given test cases.
What am I doing wrong? Why it fails anyway?
I wrote 2 different scripts, and both aren't working for some test cases
function isMerge(s, part1, part2) {
var sArr = s.split('');
var part1Arr = part1.split('');
var part2Arr = part2.split('');
var tempArr = new Array(sArr.length);
function compareArrays(arr1, arr2){
var count = 0;
for (var i = 0; i < arr1.length; i++) {
if (arr1[i] !== arr2[i]) count++;
}
return (count == 0);
}
for (var i = 0; i < sArr.length; i++) {
for (var j = 0; j < part1Arr.length; j++) {
if (sArr[i] == part1Arr[j]) tempArr[i] = j;
}
for (var k = 0; k < part2Arr.length; k++) {
if (sArr[i] == part2Arr[k]) tempArr[i] = k;
}
}
alert(tempArr);
var check = tempArr.slice();
check.sort();
alert(check);
if (compareArrays(tempArr, check)) return true;
else return false;
}
alert(isMerge('codewars', 'cdw', 'oears'));
function isMerge(s, part1, part2) {
// create arrays of letters
var sArr = s.split('');
var part1Arr = part1.split('');
var part2Arr = part2.split('');
// create an associative array 'temp' (0:C, 1:O and so on)
var temp = {};
for (var k = 0; k < sArr.length; k++) {
temp[k] = sArr[k];
}
// reverse an associative array 'temp' (now C:0, O:0 and so on)
for (var key in temp) {
var keyTemp = key;
var keyValue = temp[key];
key = keyValue;
temp[key] = keyTemp;
}
// the function that compares arrays
function compareArrays(arr1, arr2){
var count = 0;
for (var i = 0; i < arr1.length; i++) {
if (arr1[i] !== arr2[i]) count++;
}
return (count == 0);
}
// sorting function
function order(a, b) {
var comparingA;
var comparingB;
for (var char in temp) {
if (char == a) {
comparingA = temp[char]; // comparingA is the number of 'a' in object 'temp'
}
if (char == b){
comparingB = temp[char]; // comparingB is the number of 'b' in object 'temp'
}
}
return (comparingA - comparingB);
}
// create copies of arrays
var part1Sorted = part1Arr.slice();
var part2Sorted = part2Arr.slice();
// and sort that copies
part1Sorted.sort(order);
part2Sorted.sort(order);
// If the array did not change after sorting, the order of the letters was correct
if (compareArrays(part1Sorted, part1Arr) && compareArrays(part2Sorted, part2Arr)) {
// so now we can check is merge possible
sArr = sArr.sort();
var parts = part1Arr.concat(part2Arr);
parts = parts.sort();
var res = compareArrays(sArr, parts);
return res;
}
return false;
}
alert(isMerge('codewars', 'code', 'wasr'));
alert(isMerge('codewars', 'oers', 'cdwa'));
I just added comments to the second script
I find it hard to understand what your code is attempting to do. It would help if you provided comments as well as explain the idea behind the algorithm/s you are attempting to implement.
Here's a commented example of a recursion that considers if pointers i and j to parts 1 & 2 could constitute a valid merge up to that point.
function isMerge(s, part1, part2) {
// Merge is invalid if the parts' lengths don't add up to the string's
if (part1.length + part2.length != s.length)
return false;
// Recursive function
function g(i, j){
// Base case: both pointers are exactly at the end of each part
if (i == part1.length && j == part2.length)
return true;
// One of our pointers has extended beyond the part's length,
// that couldn't be right
if (i > part1.length || j > part2.length)
return false;
// Just part1 matches here so increment i
if (part1[i] == s[i + j] && part2[j] != s[i + j])
return g(i + 1, j);
// Just part2 matches here so increment j
else if (part1[i] != s[i + j] && part2[j] == s[i + j])
return g(i, j + 1);
// Both parts match here so try incrementing either pointer
// to see if one of those solutions is correct
else if (part1[i] == s[i + j] && part2[j] == s[i + j])
return g(i + 1, j) || g(i, j + 1);
// Neither part matches here
return false;
}
// Call the recursive function
return g(0,0);
}
console.log(isMerge('codewars', 'cdw', 'oears'));
console.log(isMerge('codecoda', 'coda', 'code'));
console.log(isMerge('codewars', 'oers', 'cdwa'));
console.log(isMerge('codewars', 'cdw', 'years'));
Stack version for really long strings:
function isMerge2(s, part1, part2) {
if (part1.length + part2.length != s.length)
return false;
let stack = [[0,0]];
while (stack.length){
[i, j] = stack.pop();
if (i == part1.length && j == part2.length)
return true;
if (i > part1.length || j > part2.length)
continue;
if (part1[i] == s[i + j] && part2[j] != s[i + j])
stack.push([i + 1, j]);
else if (part1[i] != s[i + j] && part2[j] == s[i + j])
stack.push([i, j + 1]);
else if (part1[i] == s[i + j] && part2[j] == s[i + j]){
stack.push([i + 1, j]);
stack.push([i, j + 1]);
}
}
return false;
}
function test(){
let s = '';
for (let i=0; i<1000000; i++)
s += ['a','b','c','d','e','f','g'][~~(Math.random()*6)];
let lr = {
l: '',
r: ''
};
for (let i=0; i<s.length; i++){
let which = ['l', 'r'][~~(Math.random()*2)];
lr[which] += s[i];
}
console.log(isMerge2(s,lr.l,lr.r));
}
test();
This is a recursive approach: it checks for a match of the first character of the string with either of the parts, and if there's a match recurses to try and match the rest of the string with the rest of the parts. The tricky thing is that when the first character of both parts is the same, you have to check if you can match against either of them (this solves the Bananas test).
function isMerge(str, p1, p2) {
if (!str.length) return !p1.length && !p2.length;
if (p1.length && str.charAt(0) == p1.charAt(0)) {
if (p2.length && str.charAt(0) == p2.charAt(0)) {
return isMerge(str.substr(1), p1.substr(1), p2) || isMerge(str.substr(1), p1, p2.substr(1));
}
else {
return isMerge(str.substr(1), p1.substr(1), p2);
}
}
else if (p2.length && str.charAt(0) == p2.charAt(0)) {
return isMerge(str.substr(1), p1, p2.substr(1));
}
else {
return false;
}
}
Recursive Call:
You could use a recursive approach by first checking the length of string and part strings and then by the length or by checking a character and by checking the rest of the given part strings.
function isMerge(s, part1, part2) {
if (s.length !== part1.length + part2.length) {
return false;
}
if (!s.length) {
return true;
}
if (part1[0] === s[0] && isMerge(s.slice(1), part1.slice(1), part2)) {
return true;
}
if (part2[0] === s[0] && isMerge(s.slice(1), part1, part2.slice(1))) {
return true;
}
return false;
}
console.log(isMerge('codewars', 'cdw', 'oears')); // true
console.log(isMerge('codewars', 'oers', 'cdwa')); // true
console.log(isMerge('codecoda', 'coda', 'code')); // true
console.log(isMerge('baeabb', 'b', 'baeab')); // true
console.log(isMerge('bdab', 'bdab', '')); // true
console.log(isMerge('bfaef', 'f', 'bfae')); // true
console.log(isMerge('codewars', 'cdw', 'years')); // false
console.log(isMerge('codewars', 'code', 'warss')); // false
console.log(isMerge('codewars', 'codes', 'wars')); // false
console.log(isMerge('', 'a', 'b')); // false
.as-console-wrapper { max-height: 100% !important; top: 0; }
With a Stack instead of a Recursive Call:
function isMerge(s, part1, part2) {
var stack = [[s, part1, part2]];
if (s.length !== part1.length + part2.length) {
return false;
}
while (stack.length) {
[s, part1, part2] = stack.shift();
if (!s.length) {
return true;
}
if (part1[0] === s[0]) {
stack.push([s.slice(1), part1.slice(1), part2]);
}
if (part2[0] === s[0]) {
stack.push([s.slice(1), part1, part2.slice(1)]);
}
}
return false;
}
console.log(isMerge('codewars', 'cdw', 'oears')); // true
console.log(isMerge('codewars', 'oers', 'cdwa')); // true
console.log(isMerge('codecoda', 'coda', 'code')); // true
console.log(isMerge('baeabb', 'b', 'baeab')); // true
console.log(isMerge('bdab', 'bdab', '')); // true
console.log(isMerge('bfaef', 'f', 'bfae')); // true
console.log(isMerge('codewars', 'cdw', 'years')); // false
console.log(isMerge('codewars', 'code', 'warss')); // false
console.log(isMerge('codewars', 'codes', 'wars')); // false
console.log(isMerge('', 'a', 'b')); // false
.as-console-wrapper { max-height: 100% !important; top: 0; }
May you try this below?
function isMerge(s, part1, part2) {
var result= true;
var total = part1 + part2;
for (var i = 0; i < s.length; i++) {
var char = s.charAt(i);
if(total.indexOf(char) === -1) {
result = false;
break;
}
}
return result;
}
First of all, here is your code just working:
function isMerge(s, part1, part2) {
var sArr = s.split('');
var part1Arr = part1.split('');
var part2Arr = part2.split('');
var tempArr = new Array(sArr.length);
function compareArrays(arr1, arr2){
var count = 0;
for (var i = 0; i < arr1.length; i++) {
if (arr1[i] != arr2[i]) count++;
}
return (count == 0);
}
for (var i = 0; i < sArr.length; i++) {
for (var j = 0; j < part1Arr.length; j++) {
if (sArr[i] == part1Arr[j]) tempArr[i] = part1Arr[j];
}
for (var k = 0; k < part2Arr.length; k++) {
if (sArr[i] == part2Arr[k]) tempArr[i] = part2Arr[k];
}
}
alert(tempArr);
if (compareArrays(tempArr, sArr)) return true;
else return false;
}
alert(isMerge('codewars', 'cdw', 'oears'));
Now, what was the problem?
for (var j = 0; j < part1Arr.length; j++) {
/* Here you assigned the index (tempArr[i] = j;) not the char */
if (sArr[i] == part1Arr[j]) tempArr[i] = part1Arr[j];
}
for (var k = 0; k < part2Arr.length; k++) {
/* Here you assigned the index (tempArr[i] = k;) not the char */
if (sArr[i] == part2Arr[k]) tempArr[i] = part2Arr[k];
}
Hope i could help you ;)
Simple merge logic:
function isMerge(s, part1, part2) {
var sArr = s.split('');
var part1Arr = part1.split('');
var part2Arr = part2.split('');
var j = 0;
var k = 0;
for (var i = 0; i < sArr.length; i++) {
if ((j < part1Arr.length) && (sArr[i] == part1Arr[j]))
j++
else if ((k < part2Arr.length) && (sArr[i] == part2Arr[k]))
k++
else
break
}
return (j == part1Arr.length && k == part2Arr.length && (j + k) == sArr.length);
}
console.log(isMerge('abcd', 'ac', 'bd'));
console.log(isMerge('abcd', 'acd', 'b'));
console.log(isMerge('abcd', 'ac', 'b'));
console.log(isMerge('abcd', 'ac', 'db'));
console.log(isMerge('abcd', 'c', 'b'));
console.log(isMerge('a', '', 'a'));
console.log(isMerge('', '', ''));
console.log(isMerge('', 'a', 'b'));
console.log(isMerge('ab', '', ''));
A math problem describes a list of numbers from 1-200, you must skip the number 1, and then for each number that follows, remove all multiples of that number from the list. Do this until you have reached the end of the list.
Here's what I have so far.
var x = []; // creating an array called x with zero numbers
for ( var i = 1; i <= 200; i++ ){
x.push(i);
};
// x now should have an array that contains the intergers from 1-200.
//looping through the array.
for ( var i = 0; i <= x.length; i++ ){ //going from 1-200
if (x[i] == 1){
continue; // skipping 1
} else {
for ( var n = i+1; n <= i; n++){ // take the number 1 index bigger than x[i]
if ( n % i == 0){ //find if the modulus of x[n] and x[i] is zeor, meaning it is divisible by x[i]
x.shift(); //remove that number
console.log(x[n]);
} else {
continue;
}
}
}
};
Instead of adding number 1 to 200 and then removing non prime numbers, try only putting prime numbers into that list. Since this is a school problem (I'm guessing) I don't want to give you the answer, but if you have more questions I can answer.
Also your nested loop will never run, go over that logic again.
Another version (a minute too late, as always ;-), one with comments
// lil' helper
function nextSet(a,n){
while(a[n] == 0) n++;
return n;
}
function setPrimes(a,n){
var k, j, r;
n = n + 1;
k = n;
while(k--)a[k] = 1;
a[0] = 0; // number 0
a[1] = 0; // number 1
// get rid of all even numbers
for(k = 4; k < n; k += 2) {
a[k] = 0;
}
// we don't need to check all of the numbers
// because sqrt(x)^2 = x
r = Math.floor(Math.sqrt(n));
k = 0;
while(k < n){
k = nextSet(a,k+1);
// a test if we had them all
if(k > r){
break;
}
// unmark all composites
for(j = k * k; j < n; j += 2*k){
a[j] = 0;
}
}
return a;
}
function getPrimes(n){
// we know the size of the input
var primearray = new Array(n);
// we don't know the size of the output
// prime-counting is still an unsolved problem
var output = [];
setPrimes(primearray, n);
for(var i = 0; i < n; i++){
if(primearray[i] == 1){
output.push(i);
}
}
return output;
}
getPrimes(200);
You can go through a full implementation of that algorithm at another primesieve.
Here is, I believe, a working example of what you want:
function isPrime(num){
if(num < 2){
return false;
}
for(var i=2,l=Math.sqrt(num); i<=l; i++){
if(num % i === 0){
return false;
}
}
return true;
}
function range(f, t){
for(var i=f,r=[]; i<=t; i++){
r.push(i);
}
return r;
}
function primeRange(from, to){
var a = range(from, to), r = [];
for(var i=0,l=a.length; i<l; i++){
var v = a[i];
if(isPrime(v)){
r.push(v);
}
}
return r;
}
var prmRng = primeRange(1, 200);
console.log(prmRng);
I solved it this way:
let numbers = new Array();
for (let i = 1; i <= 200; i++) {
numbers.push(i);
}
let primeNumbers = (num) => {
let prime = new Array();
for(let i = 0; i < num.length; i++) {
let count = 0;
for (let p = 2; p <= num[i]; p++) {
if(num[i] % p === 0 && num[i] !== 2) {
count++;
} else {
if(num[i] === 2 || count === 0 && num[i]-1 === p) {
prime[i] = num[i];
}
}
}
}
return prime.filter(Boolean);
}
console.log(primeNumbers(numbers));
I have a strange problem with my alghoritm, which work if array size less than 114468 and doesn't work if more than 114468. Browse with google chrome. Can't understand why =\ Here is the code:
Generate array:
var arr = [];
var res = [];
for (var i = 114467; i > 0; i--) {
arr.push([i - 1, i]);
}
Find first elem in array to sort:
for (var i = 0, j = arr.length; i < j && res.length == 0; i++) {
var found = false;
for (var m = 0; m < j; m++) {
if (i == m || arr[i][0] == arr[m][1] || arr[i][1] == arr[m][0]) {
found = true;
break;
}
if (!found) {
res.push(arr[m]);
arr.splice(m, 1);
}
}
}
Sorting:
do {
for (var i = 0, j = arr.length; i < j; i++) {
var resLength = res.length - 1;
if (arr[i][1] == res[resLength][0] || arr[i][0] == res[resLength][1]) {
res.push(arr[i]);
arr.splice(i, 1);
break;
}
}
} while (arr.length > 0);
On the step sorting it stops to work.
All code:
var t = function () {
var arr = [];
var res = [];
for (var i = 114467; i > 0; i--) {
arr.push([i - 1, i]);
}
var startsec = new Date().getSeconds();
var startmilsec = new Date().getMilliseconds();
document.write(startsec + '.' + startmilsec + '<br>');
for (var i = 0, j = arr.length; i < j && res.length == 0; i++) {
var found = false;
for (var m = 0; m < j; m++) {
if (i == m || arr[i][0] == arr[m][1] || arr[i][1] == arr[m][0]) {
found = true;
break;
}
if (!found) {
res.push(arr[m]);
arr.splice(m, 1);
}
}
}
do {
for (var i = 0, j = arr.length; i < j; i++) {
var resLength = res.length - 1;
if (arr[i][1] == res[resLength][0] || arr[i][0] == res[resLength][1]) {
res.push(arr[i]);
arr.splice(i, 1);
break;
}
}
} while (arr.length > 0);
var stopsec = new Date().getSeconds();
var stopmilsec = new Date().getMilliseconds();
document.write(stopsec + '.' + stopmilsec + '<br>');
var executionTime = (stopsec - startsec).toString() + "s" + (stopmilsec - startmilsec).toString() + "'ms";
document.write(executionTime + '<br>');
} ();
Do i get my memory limit?
Alright, I isolated the problem. It seems that splice(0,1) slows down astronomically when the array size increases from 114467 to 114468.
Using this custom benchmark:
var t;
function startBench(){t=new Date().getTime();}
function stopBench(){console.log(new Date().getTime()-t);}
var arr=[];
for (var i = 114467; i > 0; i--) {
arr.push([i - 1, i]);
}
var arr2=[];
for (var i = 114468; i > 0; i--) {
arr2.push([i - 1, i]);
}
startBench();
for(i=0;i<1000;i++){
arr.splice(0,1);
}
stopBench();
startBench();
for(i=0;i<1000;i++){
arr2.splice(0,1);
}
stopBench();
I get 3 ms for 114467 and 2740ms for 114468 on Chrome (1000 iterations), but 170 each on Firefox. Maybe you ought to be using a different way to remove elements? Using a variant of bubble sort may work better.
I've submitted a bug report on this. Looking at the reply, it seems to be a valid bug. Hopefully it'll be fixed.
I want to get position of dots and commas in an array.
w.wer,ads,
should give something like:
[0] > 1
[1] > 5
[2] > 9
How can this be done with javascript?
function getPositions(regex, str) {
var counts = [], m;
while (m = regex.exec(str)) {
counts.push(regex.lastIndex - m[0].length);
}
return counts;
}
// Usage:
getPositions(/[,.]/g, 'w.wer,ads,'); // => [1,5,9]
Try the following
var getAllIndexesOf = function(str, toFind) {
var all = [];
for (var i = 0; i < str.length; i++) {
if (toFind.indexOf(str[i]) >= 0) {
all.push(i);
}
}
return all;
}
var result = getAllIndexsOf("w.wer,ads,", [".", ","]);
Option 1
Simply loop through each character in the string:
var myString = "w.wer,ads,";
var matchedIndexes = [];
for (var i = 0; i < myString.length; i++) {
if (myString[i] == "," || myString[i] == ".") {
matchedIndexes.push(i);
}
}
Here's a working fiddle.
Option 2
If you wanted to get a bit fancier with it, you could create an object that stores the position and the character at that position:
var myString = "w.wer,ads,";
var matchedIndexes = [];
for (var i = 0; i < myString.length; i++) {
if (myString[i] == "," || myString[i] == ".") {
matchedIndexes.push(new myMatch(myString[i], i));
}
}
function myMatch(position, character) {
this.position = position;
this.character = character;
}
Result:
Here's a working fiddle.
function dotsAndCommas(s) {
var rv = [];
for (var i = 0; i < s.length; ++i) {
if (s[i] === '.' || s[i] === ',') rv.push(i);
}
return rv;
}
Basically, just do it. There's no shortcut way I can think of to do it with a regex.
Probably not ideal, but as a thought experiment, here is a way to do it with built-in javascript functions (no explicit loops):
var pos = mystr.split(/[,.]/).slice(0,-1).map(function(val, i, a) { return a.slice(0, i+1).join('').length+i; });
Demo: http://jsfiddle.net/jtbowden/hHSB2/
To get rid of the nested .slice() which is O(n^2):
var pos = mystr.split(/[,.]/).slice(0,-1).map(function(val, i, a) { return a[i] = val.length+(a[i-1]||-1)+1; });
Demo: http://jsfiddle.net/jtbowden/hHSB2/1/
And, yes, it is still ugly and pointless.
Yet another almost one-liner:
var q = [];
[].forEach.call('w.wer,ads,', function(v, i) { /[,.]/.test(v) ? q.push(i) : 0 } );
// q == [1,5,9]
I would loop through the string and check each character like so:
var s = "w.wer,ads,";
var arr = new Array();
for( var i = 0; i < s.length; i++ ) {
if( s.charAt(i) == "." || s.charAt(i) == "," )
arr.push(i);
}
for( var i = 0; i < arr.length; i++ ) {
document.write( arr[i] );
}
Since there're 5 solutions already, I'll write my almost one-liner.
function dotsAndCommasPositions(str) {
var i = -1;
return str.split(/[.,]/).slice(0, -1).map(function(s){ return i += s.length + 1; });
};
You can create a function that loops through all the characters and saves the index of . and ,.
function getPositionsOfDotsCommas(str) {
var result = [];
for (var i = 0; i < str.length; i++) {
if (str[i] === '.' || str[i] === ',') {
result.push(i);
}
}
return result;
}