I am trying to convert(as close to original(Python in this case)) as possible the following function from Python to JS.
def getCol(mat, col):
return [mat[i][col] for i in range(3)]
def isSubMatrixFull(mat):
n = len(mat[0])
ans = [False]*(n-2)
kernel = getCol(mat, 0) + getCol(mat, 1) + getCol(mat, 2) # O(1)
for i in range(n - 2): # O(n)
if len(set(kernel)) == 9: # O(1)
ans[i] = True # O(1)
if i < n - 3: # O(1)
kernel = kernel[3:] + getCol(mat, i + 3) # O(1)
return ans
nums = [[1, 2, 3, 2, 5, 7], [4, 5, 6, 1, 7, 6], [7, 8, 9, 4, 8, 3]]
print(isSubMatrixFull(nums))
So far, I have come to this:
function isSubMatrixFull(mat) {
let test = new Array();
function getCol(mat, col) {
for (let i in mat) {
test.push([mat[i][col]]);
// I have to find a way, to basically assign test values to [mat[i][col]]
// and then send it back to getCol(mat, i + 3)
}
}
getCol(mat, 3);
// The below would be unnecessary if I would have managed to assign test values(or
// would have found other way) to [mat[i][col]] and send back to getCol(mat, i + 3)
test = test
.map((v) => v)
.join("")
.split("")
.map(Number);
let n = mat[0].length;
let res = new Array(n - 2).fill(false);
let main = mat
.map((rowValue, rowIndex) => mat.map((val) => val[rowIndex]))
.join(",")
.split(",");
for (let i = 0; i < n - 2; i++) {
if (new Set(main).size == 9) {
res[i] = true;
}
if (i < n - 3) {
main = main.slice(3) + getCol(mat, i + 3);
}
}
// return res;
}
console.log(
isSubMatrixFull([
[1, 2, 3, 2, 5, 7],
[4, 5, 6, 1, 7, 6],
[7, 8, 9, 4, 8, 3],
])
);
The **output** of the above functions' input should be
isSubmatrixFull(numbers) = [true, false, true, false]
Please see here for more info about this question: https://leetcode.com/discuss/interview-question/860490/codesignal-intern-issubmatrixfull
Please read the comments(inside code) to see what I have done and trying to do.
This code is a direct port from the python. It works for the example provided.
const getColumn = (matrix, column) => {
return [...Array(3).keys()].map((i) => {
return matrix[i][column];
});
};
const computeKernel = (matrix) => {
return [
...getColumn(matrix, 0),
...getColumn(matrix, 1),
...getColumn(matrix, 2),
];
};
const isSubMatrixFull = (matrix) => {
const n = matrix[0].length;
const answer = [...Array(n - 2)].map((_) => {
return false;
});
let kernel = computeKernel(matrix);
[...Array(n - 2).keys()].forEach((i) => {
if (new Set(kernel).size === 9) {
answer[i] = true;
}
if (i < n - 3) {
kernel = [...kernel.slice(3), ...getColumn(matrix, i + 3)];
}
});
return answer;
};
const numbers = [
[1, 2, 3, 2, 5, 7],
[4, 5, 6, 1, 7, 6],
[7, 8, 9, 4, 8, 3],
];
console.log(isSubMatrixFull(numbers));
Related
I need to check if a number repeats itself at least three times in an array. How can I refactor it to decrease the Cognitive Complexity that Lint keeps complaining about.
Heres my code:
let array11 = [1, 3, 2, 3, 5, 6, 7, 8, 9, 0, 1];
function checkDuplicateNumber (array11) {
for (let i = 0; i < array11.length; i += 1) {
let sameNumberLoop = 0;
for (let i2 = i; i2 < array11.length; i2 += 1) {
if (array11[i] === array11[i2]) {
sameNumberLoop += 1;
if (sameNumberLoop >= 3) {
return true;
}
}
}
}
}
Instead of iterating multiple times, iterate just once, while counting up the number of occurrences in an object or Map:
let array11 = [1, 3, 2, 3, 5, 6, 7, 8, 9, 0, 1];
function checkDuplicateNumber (array) {
const counts = {};
for (const num of array) {
counts[num] = (counts[num] || 0) + 1;
if (counts[num] === 3) return true;
}
return false;
};
console.log(checkDuplicateNumber(array11));
console.log(checkDuplicateNumber([3, 1, 3, 5, 3]));
let array11 = [1, 3, 2, 3, 5, 6, 7, 8, 9, 0, 1]
let array22 = [1, 3, 2, 3, 5, 6, 7, 1, 9, 0, 1]
function checkDuplicateNumber(arr) {
const map = new Map()
return arr.some((v) => (map.has(v) ? (++map.get(v).count === 3) : (map.set(v, { count: 1 }), false)))
}
console.log(checkDuplicateNumber(array11))
console.log(checkDuplicateNumber(array22))
I'm trying to create a function that retrieves the diagonal values from a 2-d array:
input = [
[1, 2, 3, 4],
[5, 1, 2, 3],
[9, 5, 1, 2]
]
output = [[9], [5, 5], [1, 1, 1], [2, 2, 2], [3, 3], [4]]
I'm having trouble figuring out how to manipulate the indices in a nested loop... This is what I'm currently working with:
const diagonalValues = arr => {
let output = new Array(2*input.length);
for (let i = 0; i < output.length; i++) {
output[i] = [];
if (i < input.length) {
for (j = input.length-1; j>i-input.length; --j) {
console.log(i, j)
}
}
}
}
How can I accomplish this?
You could use get number of rows which is just number of arrays and number of columns which is number of elements in each inner array (assuming all arrays have the same number of elements), and based on that calculate the diagonal matrix.
const input = [
[1, 2, 3, 4],
[5, 1, 2, 3],
[9, 5, 1, 2]
]
const rows = input.length;
const columns = input[0].length;
const total = columns + rows - 1;
const result = [];
for (let i = rows - 1; i >= 0; i--) {
const row = input[i];
for (let j = 0; j < columns; j++) {
const el = input[i][j];
const pos = j + rows - i - 1;
if (!result[pos]) {
result[pos] = []
}
result[pos].unshift(el)
}
}
console.log(JSON.stringify(result))
You can do the same with reduceRight and forEach methods.
let input = [
[1, 2, 3, 4, 4],
[5, 1, 2, 8, 3],
[9, 5, 1, 2, 2],
[9, 5, 1, 2, 1]
]
const result = input.reduceRight((r, a, i) => {
a.forEach((e, j) => {
const pos = j + (input.length - i - 1)
if(!r[pos]) r[pos] = []
r[pos].unshift(e)
})
return r;
}, []);
console.log(JSON.stringify(result))
You can use this algorithm to retrieve the diagonal values from 2d-input array.
const input = [
[1, 2, 3, 4],
[5, 1, 2, 3],
[9, 5, 1, 2]]
let output = []
input.forEach(res => {
res.forEach(resp => {
// if length of array is equel to 1
if (output.filter(x => x == resp).length > 0) {
output.filter(x => x == resp)[0].push(resp)
//if length of array is greater than 1
} else if (output.filter(x => x[0] == resp).length > 0) {
output.filter(x => x[0] == resp)[0].push(resp)
} else {
let temp = []
temp.push(resp)
output.push(temp)
}
})
})
output.forEach(o => console.log(JSON.stringify(o)));
let input = [
[1, 2, 3, 4],
[5, 1, 2, 8],
[9, 5, 1, 2],
[9, 5, 1, 2],
[9, 5, 1, 2],
];
let out = [];
for (let i = 1 - input.length; i < input[0].length; i++) {
let o = [];
let y = Math.max(-i, 0);
let x = Math.max(i, 0);
while (x < input[0].length && y < input.length)
o.push(input[y++][x++]);
out.push(o)
}
out.forEach(o => console.log(JSON.stringify(o)));
I have a stepped array of elements that is filled as follows:
class Funnel{
constructor() {
this.funnelContents = [];
this.layer = 0;
}
get content() {
return this.funnelContents;
}
fill(...nums) {
let index, startIndex = 0;
for(let i = 0; i < this.funnelContents.length; i++){
while ((index = this.funnelContents[i].indexOf(' ', startIndex)) > -1 && nums.length > 0) {
this.funnelContents[i][index] = nums.shift();
startIndex = index + 1;
}
}
return nums
.splice(0, 15 - this.funnelContents.reduce((count, row) => count + row.length, 0))
.filter(num => num < 10)
.reduce((arr, num) => {
if (this.funnelContents.length) {
this.funnelContents[this.funnelContents.length - 1] = this.funnelContents[this.funnelContents.length - 1].filter(char => char !== ' ');
if ((this.funnelContents[this.layer] || []).length !== this.funnelContents[this.layer - 1].length + 1) {
this.funnelContents[this.layer] = [...(this.funnelContents[this.layer] || []), num];
} else {
this.layer++;
this.funnelContents[this.layer] = [num];
}
}
else {
this.layer++;
this.funnelContents = [...this.funnelContents, [num]];
}
}, []);
}
toString() {
let str = '', nums = '', spacesCount = 1;
for(let i = 5; i > 0; i--){
str += '\\';
for(let j = 0; j < i; j++) {
if (this.funnelContents[i - 1] !== undefined) {
if (this.funnelContents[i - 1][j] !== undefined) {
nums += this.funnelContents[i - 1][j];
} else {
nums += ' ';
}
} else {
nums += ' ';
}
}
str += nums.split('').join(' ') + '\/\n' + ' '.repeat(spacesCount);
nums = '';
spacesCount++;
}
return str.substring(0, str.length - 6);
}
}
let funnel1 = new Funnel();
let funnel2 = new Funnel();
let funnel3 = new Funnel();
let funnel4 = new Funnel();
let funnel5 = new Funnel();
let funnel6 = new Funnel();
let funnel7 = new Funnel();
funnel1.fill(5,4,3,4,5,6,7,8,9,3,2,4,5,6,7,5,6,7,8); //15 elements will be added, the rest are ignored
funnel2.fill(5,4,3,4,5,6,7,8);
funnel2.fill(9,3,2,4,5,6,7);
funnel3.fill(' ');
funnel3.fill(1,5,7);
funnel4.fill(1,2,3);
funnel4.fill(' ');
funnel4.fill(3,4,5);
funnel5.fill(1);
funnel5.fill(' ', ' ', ' ');
funnel5.fill(8,2,1);
funnel6.fill(' ',' ');
funnel6.fill(1,8,2,1);
funnel7.fill(' ',' ',' ',' ',' ');
funnel7.fill(1,8,2,1);
console.log(funnel1.toString()); // the output is as expected.
console.log(funnel2.toString()); // the same result
console.log(funnel3.toString()); // expected [ [1], [5,7] ] and it really is
console.log(funnel4.toString()); // expected [ [1], [2,3], [3,4,5] ] and it really is
console.log(funnel5.toString()); // expected [ [1], [8,2], [1] ] and it really is
console.log(funnel6.toString()); // expected [ [1], [8,2], [1] ] but got [ [], [1,8], [2], [1] ]
console.log(funnel7.toString()); // nothing is changed
Here you can see that at the very beginning of the function fill a cycle was written to insert elements that came to the input instead of spaces. I added spaces artificially, in fact, there is another function that adds them. But:
1) For some reason this does not always work, for an array in the example it does not work. With a simpler space search algorithm, it also does not work properly:
for (let i = 0; i < this.funnelContents.length; i++) {
for (let j = 0; j < this.funnelContents[i].length; j++) {
if(this.funnelContents[i][j] === ' '){
this.funnelContents[i][j] = nums.shift();
}
}
}
2) It looks very cumbersome and I would like to do something similar more elegantly. I was thinking of two for loops to find elements I need, but I still hope that I can implement insertion instead of spaces inside reduce function.
You could take a single loop and slice substrings with increasing length.
function funnel(array) {
var i = 0,
l = 0,
result = [];
while (i < array.length) result.push(array.slice(i, i += ++l));
return JSON.stringify(result);
}
console.log(funnel([1]));
console.log(funnel([1, 2]));
console.log(funnel([1, 2, 3]));
console.log(funnel([1, 2, 3, 4]));
console.log(funnel([1, 2, 3, 4, 5]));
console.log(funnel([1, 2, 3, 4, 5, 6]));
console.log(funnel([1, 2, 3, 4, 5, 6, 7]));
console.log(funnel([1, 2, 3, 4, 5, 6, 7, 8]));
console.log(funnel([1, 2, 3, 4, 5, 6, 7, 8, 9]));
console.log(funnel([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]));
console.log(funnel([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]));
console.log(funnel([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]));
console.log(funnel([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]));
console.log(funnel([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]));
console.log(funnel([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
As I understand the comment, you have an array in the given style with incrementing count, but some elements are spaces and this elemenst should bereplaced with the given data.
In this case, a nested loop for getting the result set right and another index for getting the value from the data array should work.
class Funnel {
constructor() {
this.funnelContents = [];
}
get content() {
return this.funnelContents;
}
fill(...nums) {
var i = 0,
j = 0,
l = 1,
k = 0,
target = this.funnelContents;
while (k < nums.length) {
if (!target[i]) target.push([]);
if ([undefined, ' '].includes(target[i][j])) target[i][j] = nums[k++];
if (++j === l) {
if (++i > 4) break; // max 15 elements in result set
j = 0;
l++;
}
}
}
}
var funnel = new Funnel;
funnel.fill(' ', ' ', ' ', ' ');
console.log(JSON.stringify(funnel.content));
funnel.fill(1, 2, 3, 4, 5, 6, 7, 8, 9);
console.log(JSON.stringify(funnel.content));
funnel.fill(10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20);
console.log(JSON.stringify(funnel.content));
I can do this with three loops but complexity will be O(n3), can it be done with less complexity?
Adding js fiddle code for three loops approach
var arr = [1, 2, 3, 4, 5, 6, 7, 8];
var sum = 8;
find(arr, sum);
function find(arr, sum) {
var found = false;
for (var x = 0; x < arr.length - 3; x++) {
for (var y = x + 1; y < arr.length - 2; y++) {
for (var z = y + 1; z < arr.length; z++) {
if (arr[x] + arr[y] + arr[z] == sum) {
found = true;
break;
}
}
}
}
if (found) {
console.log("found");
} else {
console.log("not found");
}
}
You could use a hash table and one loop for the array and one for the sum of two items.
function find(array, sum) {
var hash = Object.create(null);
return array.some((v, i, a) => {
a.slice(0, i).forEach(w => hash[v + w] = true);
return hash[sum - v];
});
}
console.log(find([1, 2, 3, 4, 5, 6, 7, 8], 8));
console.log(find([1, 2, 3, 4, 5, 6, 7, 8], 42));
If you like it more functional with a closure over a Set for the sum two values, then this would work, too.
function find(array, sum) {
return array.some(
(s => (v, i, a) => a.slice(0, i).reduce((t, w) => t.add(v + w), s).has(sum - v))
(new Set)
);
}
console.log(find([1, 2, 3, 4, 5, 6, 7, 8], 8));
console.log(find([1, 2, 3, 4, 5, 6, 7, 8], 42));
If I have an array like
const arr = [1, 5, 7, 5, 13, 8, 1, 7, 3, 8, 5, 2, 1, 5, 7];
What would be the best way of finding that the array starts to repeat itself? In this instance that the first three numbers and the last three numbers are in a repeating pattern.
This is a random array, the repeating could easily start at index 365 and not necessarily from the first index.
Any ideas?
Thanks in advance
This does what you're looking for...
const arr1 = [1, 5, 7, 5, 13, 8, 1, 7, 3, 8, 5, 2, 1, 5, 7];
const arr2 = [1, 5, 7, 5, 13, 8, 1, 7, 3, 8, 5, 2, 1, 4, 7];
function patternFound(arr) {
var newArray = arr.map(function(o, i) {
if (i < arr.length - 1) {
return arr[i] + "|" + arr[i + 1];
}
})
.sort();
newArray = newArray.filter(function(o, i) {
if (i < arr.length - 1) {
return (o == newArray[i + 1]);
}
});
return newArray.length > 0;
}
console.log(patternFound(arr1));
console.log(patternFound(arr2));
Basically, it creates an array of paired elements from the first array, with a pipe delimiter (["1|5", "5|7", "7|5" etc.]), sorts it and then looks for duplicates by comparing each element to the next.
There's probably a much smaller way of doing this, but I didn't want to spend time making something that was unreadable. This does what you want and does it simply and clearly.
The first array is the one you supplied, and the second has been changed so there's no matching pattern.
You could use a single loop approach with short circuit and a hash table for found pairs like
{
"1|5": true,
"5|7": true,
"7|5": true,
"5|13": true,
"13|8": true,
"8|1": true,
"1|7": true,
"7|3": true,
"3|8": true,
"8|5": true,
"5|2": true,
"2|1": true
}
The iteration stops immediately on index 12 with the other found pair 1|5.
function check(array) {
var hash = Object.create(null);
return array.some(function (v, i, a) {
var pair = [v, a[i + 1]].join('|');
return hash[pair] || !(hash[pair] = true);
});
}
console.log(check([1, 5, 7, 5, 13, 8, 1, 7, 3, 8, 5, 2, 1, 5, 7])); // true
console.log(check([1, 5, 7, 5, 13, 8, 1, 7, 3, 8, 5, 2, 1, 3, 7])); // false
A nested loop seems the simplest approach.
Make sure to offset the nested loop to save calculations:
/**
* Takes array and returns either boolean FALSE or the first index of a pattern
*
* #param {any[]} arr
* #returns {(false | number)}
*/
function findArrayPattern(arr) {
if (arr.length < 2) {
return false;
}
for (var point1 = 0; point1 < arr.length - 2; point1++) {
var p1 = arr[point1];
var p2 = arr[point1 + 1];
for (var point2 = point1 + 2; point2 < arr.length - 1; point2++) {
var p3 = arr[point2];
var p4 = arr[point2 + 1];
if (p1 == p3 && p2 == p4) {
return point1;
}
}
}
return false;
}
//TEST
var arr = [1, 5, 7, 5, 13, 8, 1, 7, 3, 8, 5, 2, 1, 5, 7];
var pattern = findArrayPattern(arr);
if (pattern !== false) {
console.log("a pattern was found at " + pattern);
} else {
console.log("no pattern was found");
}