Finding performance feasibility using array strings - javascript

Here is the requirement details:
time = ["09-13", "12-14"]
getResult(time) = false
The first performance runs from 9 a.m. to 1 p.m. and the second one starts at 12 p.m. So you won't be able to see each performance in full.
time = ["07-09", "10-12", "15-19"]
getResult(time) = true
but i could not able to get the result. I am finding challange to get it done. any one help me?
here is my try:
const getResult = (time) => {
const nums = [];
let pre = 0;
time.map((item,index) => {
item.split('-').map((v,i) => {
nums.push(+v);//converting as number
});
});
const result = nums.map((v,i) => {
if(!i) return;
console.log(v-pre)//but logically this is not works
pre = v;
})
}
//time = ["09-13", "12-14"]; //false
time = ["07-09", "10-12", "15-19"] //true
getResult(time); //should be false
Thanks in advance.

Your format is already useful for direct >= comparisons, once we split the values on their -'s. So we can simply sort the values, then check that in every case after the first one, that the start portion (before the -) is at least as large as the end portion (after the -) of the previous value. It could look like this:
const feasible = (times) => [... times]
.sort ()
.every ((t, i, a) => i == 0 || t .split ('-') [0] >= a [i - 1] .split ('-') [1])
console .log (feasible (["09-13", "12-14"]))
console .log (feasible (["07-09", "10-12", "15-19"]))
We use the i == 0 || to simply avoid testing the first of the sorted values.
This involves splitting each of the values twice (well, except the first one.) If this inefficiency bothers you, we could solve it (using more memory; there's always a tradeoff!) by splitting them and saving the result:
const feasible = (times) => [... times]
.sort ()
.map (s => s .split ('-'))
.every ((t, i, a) => i == 0 || t [0] >= a [i - 1] [1])

Turn each range into each individual hour within the range, then:
if the hour already exists in the array, return false
else, push to the array
const getResult = (ranges) => {
const filled = [];
for (const range of ranges) {
let [start, end] = range.split('-').map(Number);
while (end !== start) { // Exclusive
if (filled.includes(start)) return false;
filled.push(start);
start++;
}
}
return true;
};
console.log(getResult(["09-13", "12-14"]));
console.log(getResult(["07-09", "10-12", "15-19"]));
Your current approach doesn't look to have any notion of turning the ranges into their individual hours, or of identifying overlaps.

Sort the time slots based on their start time.
Loop over all the time slots in the sorted array and if anytime the start of a slot is less than the end of the previous one, then return false.
If the whole loop is exhausted return true.
function isValid(time) {
const sortedTime = [...time].sort((a, b) => {
const slotA = Number(a.split("-")[0]);
const slotB = Number(b.split("-")[0]);
return slotA - slotB;
});
let last;
for (let t of sortedTime) {
const [start, end] = t.split("-").map(Number);
if (last && start < last) {
return false;
}
last = end;
}
return true;
}
console.log(isValid(["12-14", "09-13"]));
console.log(isValid(["10-12", "07-09", "15-19"]));

Related

Connect 4 diagonal logic via matrix

I'm wanting to add a horizontal check for a winning move in my connect 4 game - However I'm completely flumoxed with how to proceed.
There are already working checks for horizontal and vertical.
You can see that we are checking with a regex test against a stringified version of the array matrix.
A winning vertical array looks like this:
Array and joined array passed to the test function:
A winning horiontal array looks like this:
Array and joined array passed to the horizontal test function:
A winning diagonal array looks like this:
const testWin = (arr: number[]): boolean => /1{4}|2{4}/.test(arr.join(""));
const usePlayPiece = () => {
const [board, setBoard] = useState(boardState);
const [player, setPlayerTurn] = useState(playerState);
const [gameOver, setGameOver] = useState(gameOverState);
return (col: number) => {
// Play piece (non mutating)
const newBoard = board.map((column, i) => (i === col) ? [...column, player] : column);
const row = newBoard[col].length - 1;
if (
testWin(newBoard[col]) // Did win vertically
|| testWin(newBoard.map((col) => col[row] || 0)) // Did win horizontally
// || testWin() // Did win diagonally
) {
setGameOver(true);
} else {
// Keep playing
}
setBoard(newBoard);
};
};
You could temporarily pad your columns with zeroes so they are equally sized (typically to length 6). Then turn that 2D array to a string, where the columns are separated with an extra character, and then use a one-for-all regular expression on it:
const testWin = (arr: number[][]): boolean => =>
/([12])(\1{3}|(.{5}\1){3}|(.{6}\1){3}|(.{7}\1){3})/.test(
arr.map(col => col.join("").padEnd(6, "0")).join(",")
);
And just have one call where you pass the complete board as argument:
if (testWin(newBoard)) {
// game over...
}
Keeping in with the string version of your array, you need to test the full matrix through so you will need to pad out numbers after the last entry in all columns. This will allow you to test for a consistent pattern.
Using a 6 * 7 array you would end up with 6 numbers between each number you are testing for. The regex then will look like
1\d{6}1\d{6}1\d{6}1|2\d{6}2\d{6}2\d{6}2
For the opposite direction you will swap the {6} for {4}.
This can be simplified to 1(\d{6}1){3}|2(\d{6}2){3} and 1(\d{4}1){3}|2(\d{4}2){3}
Apart from operating on strings (which I think might be faster for very large matrices), the problem can be generalized on m x n matrices:
function genMtrx (n,m, probEmp = 0.8){
return Array.from({length:n}).map(d =>
Array.from({length:m}).map(d =>
Math.random() < probEmp ? 0 : Math.random() < 0.5 ? 1 : 2
)
)
}
Normally there are 8 directions you can go (l,r,t,b,tr,tl,bl,br), but since you are testing every cell until you hit a winning condition, you would need 4 of the directions like the others posted here:
const dirs = {
right: (i,j) => [i+1, j],
top: (i,j) => [i, j+1],
topleft: (i,j) => [i-1,j+1],
topright: (i,j) => [i+1,j+1]
}
Then based on these dirs, you can generalize a winning condition:
function isWinning(mtrx, nr = 1){
let streak = 0,
nCol = mtrx.length,
nRow = mtrx[0].length,
dirKeys = Object.keys(dirs),
dirKeysL = dirKeys.length;
for (let i = 0; i < nCol; ++i){
for (let j = 0; j < nRow; ++j) {
for (let k = 0; k < dirKeysL; ++k) {
let dir = dirs[dirKeys[k]],
[nI, nJ] = dir(i,j),
cell = mtrx[nI]?.[nJ];
while (cell && cell === nr) {
if(++streak === 4) {
return true;
}
[nI, nJ] = dir(nI, nJ);
cell = mtrx[nI]?.[nJ];
}
streak = 0;
}
}
}
return false;
}
The function default tests for opponent1, but if you pass 2, it will test that one instead. Then you can do (for opponent 2):
isWinning(genMtrx(100,100,0.9),2)
I am sure the algorithm can be optimized much further. I also think if the matrix is large enough, it is better to pick random cells or do a 2D binary search. In that case you will have to put back the omitted 4 directions into the dirs object.

Validate the first or last consecutive and equal numbers

I need to validate that the first two digits of a number which are not consecutive or equal.
This is for a ReactJS project, using a conditional "for" and "if" to achieve the validation.
I have this code:
for (let i = 0; i < arrayPassword.length - 1; i++)
{
const actual = parseInt(arrayPassword[i])
const next = parseInt(arrayPassword[i + 1])
//validate consecutive and equal numbers
let isConsecutive = actual + 1 === next || actual === next ? true : false;
if (isConsecutive) {
callback(`consecutive and equal numbers are not allow.`);
} else {
callback();
}
}
At this moment I can validate the full expression when I have a 4 digits consecutive or equal, for example:
(1111, 2222, 3333, 1234, 2345, 6789, ...)
and it's ok, but the problem appears when the first two digits are equal or consecutive, for example:
(1147, 2293, 3360, 1244, 6750, 8952, ...)
I hope that through a condition the numbers like the previous examples do not enter the conditional If.
I appreciate the interest in helping me.
I believe that this simple function do what you need - checks if any two adjacent numbers in a password array are equal or consecutive
function neighboursAreEqual(arrayPassword) {
for (var i = 0; i < arrayPassword.length - 1; i++) {
const current = parseInt(arrayPassword[i]);
const next = parseInt(arrayPassword[i + 1]);
if (current === next || Math.abs(current - next) === 1) return true;
}
return false;
}
// to test how it works
console.log(neighboursAreEqual([])) // false (no symbols to compare)
console.log(neighboursAreEqual(['1'])) // false (no neighboring symbol)
console.log(neighboursAreEqual(['1', '2'])) // true (consecutive)
console.log(neighboursAreEqual(['1', '3'])) // false
console.log(neighboursAreEqual(['2', '2'])) // true(equal)
console.log(neighboursAreEqual(['0', '2', '4'])) // false
console.log(neighboursAreEqual(['0', '2', '3'])) // true (consecutive)
console.log(neighboursAreEqual(['0', '8', '2', '2'])) // true (consecutive)
If you need to compare only first two symbols in array (though I don't know the reason for such requirement) the function becomes even easier - you won't even need for there...
There's no way to know that a number is invalid before looking at all its digits, but your code calls the callback within the loop, after looking at only two. In fact, the callback is called three times, once on each iteration. To fix it, keep track of each validity check in a separate variable, and only call the callback after the loop completes:
let equalSoFar = true;
let consecutiveSoFar = true;
for (let i = 0; i < arrayPassword.length - 1; i++) {
const actual = parseInt(arrayPassword[i])
const next = parseInt(arrayPassword[i + 1])
// validate equal numbers
// "equalSoFar &&" because after a nonequal digit is encountered, equalSoFar MUST be false
equalSoFar = equalSoFar && actual === next;
// validate consecutive numbers
// "consecutiveSoFar &&": same logic
consecutiveSoFar = consecutiveSoFar && actual + 1 === next;
}
if (equalSoFar || consecutiveSoFar) {
callback(`consecutive and equal numbers are not allowed.`);
} else {
callback();
}
You can also break each validity check into its own function and examine the number array with Array.every. A number array has all equal digits if each of its digits is equal to the first one. It has all consecutive digits if each digit is equal to the first one plus its index in the array.
function allSameDigit(array) {
return array.every(digit => digit === array[0]);
}
function allConsecutiveDigits(array) {
return array.every((digit, index) => +digit === +array[0] + index);
}
function isValid(array) {
return !allSameDigit(array) && !allConsecutiveDigits(array);
}
const testValues = [1111, 2222, 3333, 1234, 2345, 6789, 1147, 2293, 3360, 1244, 6750, 8952]
.map(num => num.toString().split(''));
for (let arr of testValues)
console.log(`${arr.join('')}: ${isValid(arr) ? 'valid' : 'invalid'}`);

Generate list of items with 0 prefix using padStart

I implemented the way to generate a list of items with iterable counts with prefix 0. What is the best way to generate such kind of list?
Current behaviour:
const generateList = (length, n, i) => {
let b = n+i
return b.toString().padStart(length.toString().length + n.toString.length, 0)
}
Array(10).fill(null).map((x, i) => generateList(10,2, i))
Output result:
["002", "003", "004", "005", "006", "007", "008", "009", "010", "011"]
Do u have any idea to make it another way?
You could determine the number of characters needed at the start and used the predetermined value to format the output for the array.
function createList(startValue, endValue) {
let
// The minimum output length, for a single digit number, is 2 chars.
outputLength = 2,
testValue = 10,
// Create an empty array which has as many items as numbers we need to
// generate for the output. Add 1 to the end value as this is to be
// inclusive of the range to create. If the +1 is not done the resulting
// array is 1 item too small.
emptyArray = Array(endValue - startValue + 1);
// As long as test value is less than the end value, keep increasing the
// output size by 1 and continue to the next multiple of 10.
while (testValue <= endValue) {
outputLength++;
testValue = testValue * 10;
}
// Create a new array, with the same length as the empty array created
// earlier. For each position place a padded number into the output array.
return Array.from(emptyArray, (currentValue, index) => {
// Pad the current value to the determined max length.
return (startValue + index).toString().padStart(outputLength, '0');
});
}
function createListWithLength(length, startValue = 0) {
return createList(startValue, startValue + length);
}
console.log(createList(2,10));
console.log(createListWithLength(30));
console.log(createListWithLength(10, 995));
Have a look at generators:
function* range(from, to) {
for (var i=from; i<to; i++)
yield i;
}
function* paddedRange(from, to) {
const length = (to-1).toString(10) + 1 /* at least one pad */;
for (const i of range(from, to))
yield i.padStart(length, '0');
}
console.log(Array.from(paddedRange(2, 12)));
You can also inline the loop from range into paddedRange, or you can make it return an array directly:
function paddedRange(from, to) {
const length = (to-1).toString(10) + 1 /* at least one pad */;
return Array.from(range(from, to), i => i.padStart(length, '0'));
}
console.log(paddedRange(2, 12));
The main simplification is that you should compute the padding length only once and give it a denotative name, instead of computing it for every number again. Also ranges are usually given by their lower and upper end instead of their begin and a length, but you can easily switch back if you need the latter for some reason.
Not sure, but maybe something like this
const generateList = length => Array(length).fill('0').map((item, index) => item + index);
console.log(generateList(20));

Print the value of the first element that occurs N number of times

So I'm making this ReactJS application.
Part of a filtering system I have the following problem:
Simplified...
I have an array, let's say its simple one like let arr = [1,7,4,3,4,7];
and I also input an N variable from the user that is a simple integer input.
I need to return or console.log() the integers from the array that is repeated N times. If there is nonsuch repeating number log err msg or return -1;
For instance,
let arr = [1,7,4,3,4,7]; and let n = 2; i get 7- cos 7 repeats 2 times
let arr = [7,4,5,3,5,5,3,4,3,2,1]; and let n = 3; i get 5 - cos 5 repeats 3 times
let arr = [1,6,4,6,4,6]; and let n = 4; i get -1 or cl("err") - cos nothing repeats 4 times
Code from comments:
const getRepeatingNumber = (arr, n) => {
for (unit of arr) {
if (unit === maj_unit) count++;
else {
count--;
if (count === 0) {
maj_unit = unit;
count = 1;
}
}
}
return maj_unit;
}
You can use array#every, create an accumulator and place the number as key and its frequency as value, once the value reaches the specified number of occurrence, break from the loop using return false, then return the result.
const getRepeatingNumber = (arr, count) => {
let result = -1, hash = {};
arr.every(n => {
hash[n] = (hash[n] || 0) + 1;
if(hash[n] === count) {
result = n;
return false;
}
return true;
});
return result;
}
console.log(getRepeatingNumber([1,7,4,3,4,7],2));
console.log(getRepeatingNumber([7,4,5,3,5,5,3,4,3,2,1], 3));
console.log(getRepeatingNumber([1,6,4,6,4,6], 4));
Below is the code which will fix your problem I hope.
You need to loop over all array values and find how many time each value occurred and save number of occurrence in a result array because there may be multiple values occurred n number of times. i.e. in your provided array let arr = [1,7,4,3,4,7]; 7 and 4 occurred twice but i am returning result[0] since you might only need first occurred value for n times.
let arr = [1,7,4,3,4,7];
let getRepeatingNumber = function (arr, n) {
let result = [];
arr.forEach((value) => {
if (arr.filter(val => val === value).length === n && !result.includes(value)) {
result.push(value);
}
});
return result.length ? result[0] : -1
}
console.log(getRepeatingNumber(arr, 2)); // 7

Flipping 0's and 1's from a natural number

I want to create a javascript function to flip 1's to 0's in a natural number and I'm out of Ideas to achieve this,
Actually, I had a couple of URL's, and I replaced all 0's from a query parameter with 1's and now I no longer know the original parameter value, because there were few 1's in the original parameter value and now both are mixed, so basically I screwed myself,
The only solution for me is to try flipping each 1 to 0 and then 0's to 1's and test each number as the parameter.
This is the parameter value (after replacing 0's with 1's)
11422971
using above input I want to generate numbers as follows and test each of these
11422970
10422971
10422970
01422971
As you can see only 1's and 0's are changing, the change according to binary,
Each position in your string can be one of n characters:
A "0" can be either "0" or "1"
A "1" can be either "0" or "1"
Any other character c can only be c
We can store this in an array of arrays:
"11422971" -> [ ["0", "1"], ["0, "1"], ["4"], ... ]
To transform your string to this format, you can do a split and map:
const chars = "11422971"
.split("")
.map(c => c === "1" || c === "0" ? ["1", "0"] : [ c ]);
Once you got this format, the remaining logic is to create all possible combinations from this array. There are many ways to do so (search for "array combinations" or "permutations"). I've chosen to show a recursive pattern:
const chars = "11422971"
.split("")
.map(c =>
c === "1" || c === "0"
? ["1", "0"]
: [ c ]
);
const perms = ([ xs, ...others ], s = "", ps = []) =>
xs
? ps.concat(...xs.map(x => perms(others, s + x, ps)))
: ps.concat(s);
console.log(perms(chars));
you can do it with a number like a string, and after parse it, something like that
var number= "12551";
number= number.replace("1","0");
The result of number will be "02550"
after that parse number to int
This will generate all permutations.
const generatePermutations = (number) => {
let digits = number.split('');
// find out which digits can be flipped
let digitPositions = digits.reduce((acc, val, i) => {
if (val === '0' || val === '1') acc.push(i);
return acc;
}, []);
// we're going to be taking things in reverse order
digitPositions.reverse();
// how many digits can we flip
let noBits = digitPositions.length;
// number of permutations is 2^noBits i.e. 3 digits means 2^3 = 8 permutations.
let combinations = Math.pow(2, digitPositions.length);
let permutations = [];
// for each permutation
for (var p = 0; p < combinations; p++) {
// take a copy of digits for this permutation
permutations[p] = digits.slice();
// set each of the flippable bits according to the bit positions for this permutation
// i = 3 = 011 in binary
for (var i = 0; i < noBits; i++) {
permutations[p][digitPositions[i]] = '' + ((p >> i) & 1);
}
permutations[p] = permutations[p].join('');
}
return permutations;
};
console.log(generatePermutations('11422970'));
In case your looking for a recursive approach:
function recursive(acc, first, ...rest) {
if(!first) return acc;
if(first == '0' || first == '1') {
var acc0 = acc.map(x => x + '0');
var acc1 = acc.map(x => x + '1');
return recursive([].concat(acc0, acc1), ...rest);
} else {
return recursive(acc.map(x => x + first), ...rest);
}
}
recursive([''], ...'11422971')
// output ["00422970", "10422970", "01422970", "11422970", "00422971", "10422971", "01422971", "11422971"]
This just counts in binary and fills out a template for each value.
function getPossibleValues(str) {
function getResult(n) {
let nIndex = 0;
const strValue = n.toString(2).padStart(nDigits, '0');
return str.replace(rxMatch, () => strValue.charAt(nIndex++));
}
const rxMatch = /[01]/g;
const nDigits = str.length - str.replace(rxMatch, '').length;
const nMax = Math.pow(2, nDigits);
const arrResult = [];
for(let n = 0; n<nMax; n++) {
arrResult.push(getResult(n));
}
return arrResult;
}
console.log(getPossibleValues('11422970'));
Thank you all to respond, you saved my life, btw the approach I used was,
0- convert the number into a string. (so we can perform string operations like split())
1- count the number of 1's in the string (let's say the string is "11422971", so we get three 1's, I used split('1')-1 to count)
2- generate binary of three-digit length,(ie from 000 to 111). three came from step 1.
2- break down the string to single chars, (we'll get
array=['1','1','4','2','2','9','7','1'] )
3- take the first binary number (ie b=0b000)
4- replace first 1 from the character array with the first binary digit of b (ie replace 1 with 0), similarly replace second 1 with the second binary digit of b and so on.
5- we'll get the first combination (ie "00422970")
5- repeat step 3 and 4 for all binary numbers we generated in step 2.

Categories