Reset loop with condition - javascript

I'm coding project with codewars and have a trouble. This function has to check if parentheses in string are valid. My problem is when i assign i = 0 in function valid pair it's doesnt work . I tried assign it below if statement and then my loop is infinite. My idea is valid pairs and then start over and over while parens.lenght will be <= 2 then i know that there left a pair and i can nothing to do with it. Could you help me?
// "()" => true
// ")(()))" => false
// "(" => false
// "(())((()())())" => true
const validPair = (parens) => {
console.log(parens);
for (let i = 0; i < parens.length; i++) {
if (parens[i] == '(' && parens[i + 1] == ')') {
parens.splice(i, 2);
i = 0;
console.log(parens.length);
}
if (parens.length <= 2) {
console.log(parens);
}
}
console.log(parens);
};
function validParentheses(parens) {
let validLength = 0;
parens = parens.split('');
parens.forEach((el) => {
if (el == '(') {
validLength++;
} else {
validLength--;
}
});
if (validLength != 0) {
return false;
} else {
validPair(parens);
}
}
console.log(validParentheses('(()))(()'));
I will be updating this code but i stuck in this moment and dont know what to do.

You've overcomplicated things
Simply counting is enough
// "()" => true
// ")(()))" => false
// "(" => false
// "(())((()())())" => true
function validParentheses(parens) {
let lefts = 0
for (const par of parens) {
if (par === '(') {
lefts += 1
} else {
lefts -= 1
}
if (lefts < 0) return false
}
if (lefts !== 0) return false
return true
}
console.log(validParentheses('()'));
console.log(validParentheses('(()))(()'));
console.log(validParentheses('('));
console.log(validParentheses('(())((()())())'));

Approach:
The idea is to use stack. so when you get ( you need to push it to stack and when you get ) you need to pop it. And at the end just need to check stack is still empty or not. if stack is empty that means parentheses are valid.
Edge case:
When there's no element in the stack and you have ) that means there's no ( bracket to match with it. so it returns false.
const validParentheses = (str) => {
const stack = [];
for (let parentheses of str) {
if (parentheses == '(') stack.push(parentheses);
else {
if (!stack.length) return false;
stack.pop();
}
}
return !stack.length;
}
console.log(validParentheses("()"));
console.log(validParentheses(")(()))"));
console.log(validParentheses("("));
console.log(validParentheses("(())((()())())"));

Related

Javascript. I am trying to use .findIndex() but I get the following message: TypeError: a is not a function

I have ‘array magazine’ and ‘string ransomNote’. And want to access an element of the array based on an element from the string.
This is what I am trying: magazine.findIndex(ransomNote[i])
var canConstruct = function(ransomNote, magazine){
magazine = magazine.split('');
//console.log(magazine);
for(let i = 0; i < ransomNote.length; i++){
if (magazine.includes(ransomNote[i])){
element_to_erase = magazine.findIndex(ransomNote[i]);
magazine = magazine.splice(element_to_erase , 1);
//console.log(magazine);
continue;
} else {
return false;
}
}
return true;
};
console.log(canConstruct('aa', 'aab'));
findIndex takes a function as an argument, and you are passing it a string
you need to do
magazine.findIndex((magazineString) => magazineString === ransomNote[i])
Or just use indexOf as its pointed in the comments, and probably validate if that returns something other than -1 (indexOf) or undefined (findIndex) in either case.
Instead of converting the input string magazine to an array, you can use magazine.search directly -
const removeCharAt = (str = 0, pos = 0) =>
str.substr(0, pos) + str.substr(pos + 1)
const canConstruct = (ransomNote = "", magazine = "") => {
for (const char of ransomNote) {
const pos = magazine.search(char)
if (pos >= 0)
magazine = removeCharAt(magazine)
else
return false
}
return true
}
console.log(canConstruct('aa', 'aab'))
// true
console.log(canConstruct('az', 'aab'))
// false
console.log(canConstruct('stay inside', 'diet coke on sale this sunday'))
// true
There is another issue. While splice, u don't need to reassign. Splice modify the same array.
You can simplify using array.every.
var canConstruct = function (ransomNote, magazine) {
magazine = magazine.split("");
return ransomNote.split("").every((rChar) => {
const rIndex = magazine.indexOf(rChar);
if (rIndex !== -1) {
magazine.splice(rIndex, 1);
return true;
}
});
};
console.log(canConstruct("aa", "aab"));
console.log(canConstruct("az", "aab"));

Structure order of an array based on an item in the array has certain value set to true

I have incoming data from my CMS that all contains the value featuredProject, it can be true, null or false. If it's true I add a css class that makes it span across the screen. To help keep my grid structure intact I need to always have at least two projects with featuredProject set to false or null before or after a project with featuredProject set to true.
The problem is that the data from the CMS doesn't respect the supposed design of the grid and can and probably will come out distorted from what I need to loop it out correctly.
What I've been trying to achive now is a function that loops over all projects in the array from the CMS and looks at what the featuredProject value is on the current project in the loop. If it's set to true I look back at the past 2 indexes of the array to see what their featuredProject value is. If both of them doesn't have the value set to false or null I want to sort the array by shifting the current index one step forward, exit the loop and then loop over it again to check if all the values are in order.
Right now I get an error where one value of the array is undefined which I don't really get why.
An image displaying the grid I would like to achieve.
https://imgur.com/a/KrdwlNI
The code I have right now.
The function to move an index
function move(array: any[], from: number, to: number) {
if (to === from) return array
const target = array[from]
const increment = to < from ? -1 : 1
for (let k = from; k != to; k += increment) {
array[k] = array[k + increment]
}
array[to] = target
return array
}
The function that checks the value of featuredProject
const sortImageGrid = (arr: any[]): any[] => {
// console.log(arr)
const sortedArr: any[] = []
arr.map((item, index) => {
if (item === undefined) {
return
}
// console.log(item)
if (index === 0) {
sortedArr.push(item)
} else if (index === 1 && item.featuredProject) {
move(arr, index, index + 1)
} else if (index === 1 && !item.featuredProject) {
sortedArr.push(item)
} else if (
item.featuredProject &&
!arr[index - 1].featuredProject &&
!arr[index - 2].featuredProject
) {
sortedArr.push(item)
} else if (
(item.featuredProject && arr[index - 1].featuredProject === true) ||
(item.featuredProject && arr[index - 2].featuredProject === true)
) {
move(arr, index, index + 1)
} else {
sortedArr.push(item)
}
console.log(sortedArr)
})
if (sortedArr.length === arr.length) {
return sortedArr
} else {
return []
}
}
How I run the function in the component where I map out the projects
const [gridSorted, setGridSorted] = useState(false)
let sortedArr: any[] = []
if (data.allSanityProject.nodes && data.allSanityProject.nodes.length > 0) {
while (gridSorted === false) {
sortedArr = sortImageGrid(data.allSanityProject.nodes)
// console.log(sortedArr)
if (sortedArr.length === data.allSanityProject.nodes.length) {
setGridSorted(true)
return
}
}
}
One way I'm thinking you could do this is filter out the responses into separate arrays:
let falsyValues = [];
let truthyValues = []
for(let item of arr){
if(item.featuredProject){
truthyValues.push(item)
} else {
falsyValues.push(item)
}
}
Then create a function that "zips" them based on your criteria:
function zip(falsyArr, truthyArr){
let zippedValues = []
for(let i = 0, n = 0; i < falsyArr.length; i++, n += 2){
if(!falsyArr[n] || !falsyArr[n+1]){
return zippedValues
}
zippedValues.push(falsyArr[n], falsyArry[n+1], truthyArr[i])
}
return zippedValues;
}
You'll just have to slightly adjust the zip function to account for arrays with differing lengths and determine which one you want to loop over (probably the max length between the two).

RangeError: Maximum call stack size error in JS function

function findRandomDayIndex() {
var dayindex = _.random(0, 39);
var slot = dayslots[dayindex]; // array of 40 objects
if(slot.filled === true || slot === undefined) {
return findRandomDayIndex();
} else {
return dayindex;
}
}
I get the error:
RangeError: Maximum call stack size exceeded iteration of the same function
How can better write the function?
You might try out this version
function findRandomDayIndex()
{
var dayindex = Math.random(0, 39);
while( ( slot = dayslots[dayindex] ) == null )
dayindex = Math.random(0, 39);
return dayindex ;
}
Please check the consistence of dayslot, in order to prevent infinite while-looping anyway
You don't need recursion to do this. With a little refactoring you can map your array to save indexes, then filter undefined and filled values and then get random item from that new array, e.g.:
function findRandomDayIndex() {
var notFilled = dayslots.map(function(value, index) {
return {
value: value,
index: index
};
}).filter(function(day) {
return day.value !== undefined && !day.value.filled;
});
if (!notFilled.length) {
// all items of dayslots are undefined or filled
// handle this case here
}
var dayindex = _.random(0, notFilled.length - 1);
return notFilled[dayindex].index;
}
Here's one that handles when all dayslots are filled. The sample code returns -1, but you can update it to return anything.
function findRandomDayIndex() {
// Create an array of indexes for items that aren't filled
var m = _.map(dayslots, function(v, k) {
if (!v.filled) {
return k;
}
});
// The array will have undefined for those already filled, so we use lodash without to remove them
m = _.without(m, undefined);
if (m.length === 0) {
// Handle when all slots are filled
return -1;
} else {
// return a random index
return m[_.random(0, m.length)];
}
}
// Lets create some test data
var dayslots = [];
for (var i = 0; i < 40; i++) {
dayslots.push({
filled: false
});
}
// Test our function
console.log(findRandomDayIndex());
If you change the filled to true, then you'll get a -1.
As #CodeiSir commented since no slot was free code was going into infinite loop. So I changed the code as below and it works fine. Thank you!
if (_.findWhere(dayslots, {filled: false}) !== undefined) {
var dayindex = _.random(0, 39);
var slot = dayslots[dayindex];
console.log(dayslots.length);
if(slot.filled === true || slot === undefined) {
return findRandomDayIndex();
} else {
return dayindex;
}
}

Search in json array

I am new to json. My json array
[{"seat_number":"834"},{"seat_number":"8F3"},{"seat_number":"891"},
{"seat_number":"814"},{"seat_number":"4081"},{"seat_number":"8F1"},
{"seat_number":"8F9"},{"seat_number":"4039"},{"seat_number":"0"},
{"seat_number":"509"},{"seat_number":"662"},{"seat_number":"561"},
{"seat_number":"791"},{"seat_number":"849"}]
I want to find seat number already exist in array.
if(($scope.employeeSeat.seat_array.indexOf($scope.employeeData.new_seat_number))>-1)
{
alert('Seat number is already assigned.Please Check');
}
What is wrong?
You can do it the hard way if you want
var data = [
{"seat_number":"834"},{"seat_number":"8F3"},
{"seat_number":"891"},{"seat_number":"814"},
{"seat_number":"4081"},{"seat_number":"8F1"},
{"seat_number":"8F9"},{"seat_number":"4039"},
{"seat_number":"0"},{"seat_number":"509"},
{"seat_number":"662"},{"seat_number":"561"},
{"seat_number":"791"},{"seat_number":"849"}
];
function matchingSeat(x) {
return data.some(function(elem) {
return elem.seat_number === x;
});
}
console.log("831", matchingSeat("834")); // 834 true
console.log("8F3", matchingSeat("8F3")); // 8F3 true
console.log("999", matchingSeat("999")); // 999 false
I think that's a total pain tho. Once you have some reusable utility functions at your disposal, this problem becomes a lot easier to solve.
Don't freak out: the ES5 solution is below
// ES6
// reusable lib
let prop = y => x => x[y];
let comp = f => g => x => f(g(x));
let eq = y => x => x === y;
let some = f => xs => xs.some(f);
Now write some helpers to solve your task
let eqSeat = x => comp(eq(x))(prop("seat_number"));
let hasSeat = comp(some)(eqSeat);
Check it out
console.log("831", hasSeat("834")(data)); // 831 true
console.log("8F3", hasSeat("8F3")(data)); // 8F3 true
console.log("999", hasSeat("999")(data)); // 999 false
Here's the same code in ES5
// ES5
// reusable lib
var prop = function prop(y) {
return function (x) {
return x[y];
};
};
var comp = function comp(f) {
return function (g) {
return function (x) {
return f(g(x));
};
};
};
var eq = function eq(y) {
return function (x) {
return x === y;
};
};
var some = function some(f) {
return function (xs) {
return xs.some(f);
};
};
Your helpers
var eqSeat = function eqSeat(x) {
return comp(eq(x))(prop("seat_number"));
};
var hasSeat = comp(some)(eqSeat);
Try it out
console.log("831", hasSeat("834")(data)); // 831 true
console.log("8F3", hasSeat("8F3")(data)); // 8F3 true
console.log("999", hasSeat("999")(data)); // 999 false
Note that ({a:10}) !== ({a:10}), therefore your indexOf won't work. See this related question.
Knowing that, there are several ways to do this (assuming typeof $scope.employeeData.new_seat_number === 'string'):
1) Use a for-loop:
for (var i = 0; i < $scope.employeeSeat.seat_array.length; i++) {
if ($scope.employeeSeat.seat_array[i].seat_number === $scope.employeeData.new_seat_number) {
alert('Seat number is already assigned.Please Check');
}
}
2) Use .some:
if ($scope.employeeSeat.seat_array.some(function(seat) { return seat.seat_number === $scope.employeeData.new_seat_number; })) {
alert('Seat number is already assigned.Please Check');
}
Note: .some is the best answer other than the good old for-loop in my opinion. The rest are explorations of the Array API.
3) Use .findIndex:
if ($scope.employeeSeat.seat_array.findIndex(function(seat) { return seat.seat_number === $scope.employeeData.new_seat_number; }) !== -1) {
alert('Seat number is already assigned.Please Check');
}
4) Use .find:
if ($scope.employeeSeat.seat_array.find(function(seat) { return seat.seat_number === $scope.employeeData.new_seat_number; })) {
alert('Seat number is already assigned.Please Check');
}
Note: .find and .findIndex are experimental technologies. Check their compatibility tables to see if .find is available for your browser. If not, use their poly fills.
5) Use .map:
if ($scope.employeeSeat.seat_array.map(function(seat) { return seat.seat_number; }).indexOf($scope.employeeData.new_seat_number)) !== -1) {
alert('Seat number is already assigned.Please Check');
}
Note: Using .map will be much slower as .map iterates through the array running a function that will be used to create a brand new array.
for(var i = 0; i < myjson.length; i++)
{
if(myjson[i].seat_number == expectednumber)
{
alert('Seat number is already assigned.Please Check')
}
}
the item in your array is a object, so you can not use indexOf to find the seat_number.
you can try:
for(var i=0; i<$scope.employeeSeat.seat_array.length; ++i){
if($scope.employeeData.new_seat_number == $scope.employeeSeat.seat_array[i].seat_number){
alert('Seat number is already assigned.Please Check');
}
}
You can use array find prototype. You may find detail on this method here.
Example:
var items = [{"seat_number":"834"},{"seat_number":"8F3"},{"seat_number":"891"},{"seat_number":"814"},{"seat_number":"4081"},{"seat_number":"8F1"},{"seat_number":"8F9"},{"seat_number":"4039"},{"seat_number":"0"},{"seat_number":"509"},{"seat_number":"662"},{"seat_number":"561"},{"seat_number":"791"},{"seat_number":"849"}];
var newSeatNumber = $scope.employeeData.new_seat_number;
var result = items.find(function(item) {return item.seat_number == newSeatNumber ;})
console.log(result); // Object { seat_number="791"} if found, else undefined
if(result)
{
//Do your stuff. Item exits in array
}
Try this. Make use of filter(). If arr is empty, then it should show the alert.
var arr=$scope.employeeSeat.seat_array.filter(function(item){
return $scope.employeeData.new_seat_number == item.seat_number;
})
if(!arr.length){
alert('Seat number is already assigned.Please Check');
}
You can also make use of some().
var bool=$scope.employeeSeat.seat_array.some(function(item){
return $scope.employeeData.new_seat_number == item.seat_number;
})
if(bool){
alert('Seat number is already assigned.Please Check');
}
Why do you need such a JSON Structure ?
I've changed the structure a little.
{
"seat_numbers" :["894", "900", "814", "591", "789", ..]
}
Load your JSON using javascript/jquery
var yourSearchingSeat = "900";
$.getJSON( "ajax/test.json", function( data ) {
var seat_numbers = data.seat_numbers
for (i=0; i<seat_numbers.length;i++) {
if (seat_numbers[i] == yourSearchingSeat) {
console.log("Seat number"+yourSearchingSeat+" already exists");
}
}
});

Check for continuous order in array in javascript

var userInput = prompt('enter number here');
var number = new Array(userInput.toString().split(''));
if (number ????){ //checks if the number is in a continuous stream
alert(correct);
}
else{
alert(invalid);
}
In Javascript, what can I do at "????" to check if it is in a continuous order/stream? Also how can I do this so that it only checks for this order/stream after a specific index in the array? Meaning the user enters say "12345678901234" which would pop up correct, but "12347678901234" would pop up invalid?(note there are two 7's) For the second part "3312345678901234" would pop up correct, how can this be implemented?
You can make a function that checks any string for a stream of continuous/increasing alpha-numeric characters starting at a given index like this:
function checkContinuous(str, startIndex) {
startindex = startIndex || 0;
if (str.length <= startIndex) {
return false;
}
var last = str.charCodeAt(startIndex);
for (var i = startIndex + 1; i < str.length; i++) {
++last;
if (str.charCodeAt(i) !== last) {
return false;
}
}
return true;
}
If it's numbers only and wrapping from 9 back to 0 is considered continuous, then it's a little more complicated like this:
function checkContinuous(str, startIndex) {
// make sure startIndex is set to zero if not passed in
startIndex = startIndex || 0;
// skip chars before startIndex
str = str.substr(startIndex);
// string must be at least 2 chars long and must be all numbers
if (str.length < 2 || !/^\d+$/.test(str)) {
return false;
}
// get first char code in string
var last = str.charCodeAt(0);
// for the rest of the string, compare to last code
for (var i = 1; i < str.length; i++) {
// increment last charCode so we can compare to sequence
if (last === 57) {
// if 9, wrap back to 0
last = 48;
} else {
// else just increment
++last;
}
// if we find one char out of sequence, then it's not continuous so return false
if (str.charCodeAt(i) !== last) {
return false;
}
}
// everything was continuous
return true;
}
Working demo: http://jsfiddle.net/jfriend00/rHH4B/
No need for arrays, just back though the string one character at a time.
When you hit a 0, substitute 10, and continue until the number
is not one more than the previous one.
function continuousFromChar(str, start){
start= start || 0;
var i= 0, L= str.length, prev;
while(L){
c= +(str.charAt(-- L)) || 10; // use 10 for 0
prev=+(str.charAt(L- 1));
if(c-prev !== 1) break;
}
return start>=L;
}
var s= "3312345678901234";
continuousFromChar(s,2)
/* returned value: (Boolean)
true
*/
This will do the checking in real-time entry, but a similar principle could be used to check an entry on a button submit or similar. I was not 100% sure as to which way you wanted it, so I went for the live method.
HTML
<input id="stream" type="text" />
Javascript
window.addEventListener("load", function () {
document.getElementById("stream").addEventListener("keyup", function (evt) {
var target = evt.target;
var value = target.value;
var prev;
var last;
var expect;
target.value = value.replace(/[^\d]/, "");
if (value.length > 1) {
prev = parseInt(value.slice(-2, -1), 10);
last = parseInt(value.slice(-1), 10);
expect = prev + 1;
if (expect > 9) {
expect = 0;
}
if (last !== expect) {
target.value = value.slice(0, value.length - 1);
}
}
}, false);
});
On jsfiddle
By changing the value here
if (value.length > 1) {
You can change where the checking starts.
Update: Ok, so it is function that you want, and you insist that it splits the string into an array. Then using the above as a reference, you could convert it to something like this.
Javascript
window.addEventListener("load", function () {
var testStrings = [
"0123456789012",
"0123456789",
"0123455555",
"555012345678901234",
"0123455555"];
function test(string, offset) {
if (typeof string !== "string" || /[^\d]/.test(string)) {
return false;
}
var array = string.split("");
var prev;
var last;
var expect;
return !array.some(function (digit, index) {
if (index >= offset) {
prev = parseInt(array[index - 1], 10);
last = parseInt(digit, 10);
expect = prev + 1;
if (expect > 9) {
expect = 0;
}
if (last !== expect) {
return true;
}
}
return false;
});
}
testStrings.forEach(function (string) {
console.log(string, test(string, 1));
});
});
On jsfiddle
As your question does not fully specify all possibilities, the above will return true for an empty string (""), of course you can simply add a check at the very beginning for that.
I also do not perform any checking for a valid number for your offset, but again this is something simple that you can add.
Of course these are just one (two) of many possible solutions, but hopefully it will set your mind in the right direction of thought.
There are some good answers here, but I would like to show a slight variation. I think it is important to showcase some different aspects of JavaScript and separating interests in code.
Functions as first class objects are cool - the exact rules for "continuous" can be changed with only changing the predicate function. Perhaps we should allow skipping numbers? No problem. Perhaps we allow hex digits? No problem. Just change the appropriate follows function for the specific rules.
This can be implemented generically because strings support indexing. This will work just as well over other array-like objects with an appropriate follows function. Note that there are no string-specific functions used in the continuous function.
Code also on jsfiddle:
// returns true only iff b "follows" a; this can be changed
function follows_1Through9WithWrappingTo0(b,a) {
if (b === "1" && a === undefined) {
// start of sequence
return true;
} else if (b === "0" && a === "9") {
// wrap
return true;
} else {
// or whatever
return (+b) === (+a) + 1;
}
}
function continuous(seq, accordingTo, from) {
// strings can be treated like arrays; this code really doesn't care
// and could work with arbitrary array-like objects
var i = from || 0;
if ((seq.length - i) < 1) {
return true;
}
var a = undefined;
var b = undefined;
for (; i < seq.length; i++) {
b = seq[i];
if (!accordingTo(b, a)) {
return false; // not continuous
}
a = b;
}
return true;
}
function assert(label, expr, value) {
if (!(expr === value)) {
alert("FAILED: " + label);
}
}
var follows = follows_1Through9WithWrappingTo0;
assert("empty1", continuous("", follows), true);
assert("empty2", continuous("foobar", follows, 6), true);
assert("skip", continuous("331234", follows, 2), true);
assert("good 1", continuous("123456789", follows), true);
assert("good 2", continuous("12345678901234", follows), true);
assert("bad seq 1", continuous("12347678901234", follows), false);
assert("bad seq 2", continuous("10", follows), false);
// here a different predicate ensures all the elements are the same
var areAllSame = function (b, a) {
return a === undefined || a === b;
};
assert("same", continuous("aaaaa", areAllSame), true);
Note that the skipping could also be extracted out of the continuous function: in a language with better "functional" collection support, such as C#, this is exactly what I'd do first.

Categories