I have 3 arrays of size n :
let values = [5, 5, 5, 6, 7, 6, 5, 4, 3, 3, 4, 5, 5];
let min_arr = [3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3];
let max_arr = [7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7];
let n = values.length;
I am trying to spot the indexes of the start and end of the "zones" where min_arr[i] > values[i] or max_arr[i] < values[i].
In the previous arrays the first zone : values[4] = 7 > 6 = max_arr[4] and !(values[5] = 6 > 6 = max_arr[5]) thus start = 4 and end = 5.
In the previous arrays the second zone : values[8] = 3 < 4 = min_arr[8] and values[9] = 3 < 4 = min_arr[9] but !(values[10] = 4 < 3 = min_arr[10]) thus start = 8 and end = 10.
In the previous arrays the expected output would be : [[4, 5], [8, 10]].
Nb: It does not matter if the last end index is greater than n.
Here is my code so far :
let temp = values
.map((e, i) => min_arr[i] > e || max_arr[i] < e ? i : undefined)
.filter((e) => e);
console.log(temp);
Output
[4, 8, 9]
My logic here is to first get all the indexes where the value is not in the bounds before removing consecutive values and adding the end value.
let res = [];
let start = temp[0];
for (let i = 0; i < temp.length; i++) {
if (i + 1 == temp.length) {
res.push([start, temp[i] + 1]);
break;
}
if (temp[i] + 1 != temp[i + 1]) {
res.push([start, temp[i] + 1]);
start = temp[i + 1];
}
}
console.log(res);
Output
[[4, 5], [8, 10]]
As a beginner in JavaScript, I find this method crude and I believe that the whole process can be done in a declarative way. How should I proceed ?
Please don't hesitate to edit this question if it lacks clarity.
Here are not so crude methods, probably they are in declarative way, also with handling some corner-cases. Corner cases i mean in result you are expencting arrays with 2 elements, open index and close index. So if the last element in array is out of bound - still an array with 2 elements will be returned. And at this point im curious about the correct output, probably you have to clarify it a bit. I assumed that indexes shoult be "first index of item that is out of bounds" and "last index of item that is out of bounds", not the "last index + 1"
let values = [5, 5, 5, 6, 7, 6, 5, 4, 3, 3, 4, 5, 5];
let min_arr = [3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3];
let max_arr = [7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7];
function getZones(values, predicateFn) {
const reduced = values.reduce(
(acc, curr, i) => {
const isMet = predicateFn(curr, i);
if (isMet) {
if (acc._zone) acc._zone[1] = i;
else acc._zone = [i];
} else if (acc._zone) {
// -1 due to current index is ok, previous was not.
acc._zone[1] = i - 1;
acc.result.push(acc._zone);
acc._zone = undefined;
}
return acc;
},
{
_zone: undefined,
result: []
}
);
// Case when zone was opened and not closed.
// Happens for last item only.
const { _zone, result } = reduced;
if (_zone) {
if (_zone.length === 1) {
_zone[1] = _zone[0];
}
result.push(_zone);
}
return result;
}
const res = getZones(values, (curr, i) => curr < min_arr[i] || curr > max_arr[i]);
console.log(JSON.stringify(res));
Test set i checked with:
const testSets = [
{
// Original set
values: [5, 5, 5, 6, 7, 6, 5, 4, 3, 3, 4, 5, 5],
min_arr: [3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3],
max_arr: [7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7]
// [[4,4],[8,9]]
},
{
// with starting and ending outs
values: [2, 5, 5, 6, 7, 6, 5, 4, 3, 3, 4, 5, 10],
min_arr: [3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3],
max_arr: [7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7]
// [[0,0],[4,4],[8,9],[12,12]]
},
{
// with starting and ending "wave"
values: [2, 8, 2, 6, 7, 6, 5, 4, 3, 3, 4, 2, 2, 10],
min_arr: [3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3],
max_arr: [7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7]
// [[0,2],[4,4],[8,9],[11,13]]
},
{
// Completely out of bounds
values: [2, 2, 2, 2, 2, 2, 2, 8, 8, 8, 8, 8, 8, 8],
min_arr: [3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3],
max_arr: [7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7]
// [[0,13]]
}
];
Also, here is a codesandbox so you can check the correct output and behavior.
Your method will work only for 2 consecutive values , try an example with more consecutive values like [[2, 7], [8, 11]] and check your method .
The solution can be the next:
let values = [5, 5, 5, 6, 7, 6, 5, 4, 3, 3, 4, 5, 5];
let min_arr = [3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3];
let max_arr = [7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7];
const result = values
.map((num, i) => (min_arr[i] > num || max_arr[i] < num ? i : undefined))
.reduce((previousValue, currentValue, currentIndex, array) => {
if ((currentValue || currentValue === 0) && !array[currentIndex - 1]) {
const findNextSibling = array.slice(currentIndex).findIndex(i => !i);
const finalConsecutiveValue =
findNextSibling === -1 || findNextSibling === 0
? 1
: findNextSibling + currentIndex;
previousValue.push([currentValue, finalConsecutiveValue]);
}
return previousValue;
}, []);
console.log('result', result);
Related
convert 9x9 array into 9 (3x3) array in javascript?
i have written the code, but its not pushing the 3x3's into separate array.
i want 9 3x3 arrays
let array =
[
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[2, 3, 1, 5, 6, 4, 8, 9, 7],
[3, 1, 2, 6, 4, 5, 9, 7, 8],
[4, 5, 6, 7, 8, 9, 1, 2, 3],
[5, 6, 4, 8, 9, 7, 2, 3, 1],
[6, 4, 5, 9, 7, 8, 3, 1, 2],
[7, 8, 9, 1, 2, 3, 4, 5, 6],
[8, 9, 7, 2, 3, 1, 5, 6, 4],
[9, 7, 8, 3, 1, 2, 6, 4, 5]
];
let final=[];
let row = [0,1,2];
let col = [0,1,2];
let counter = 0;
for ( let i = 0 ; i <= array.length - 1 ; i += 3 )
{
for(let j = 0 ; j <= array.length - 1 ; j += 3 )
{
final.push([]);
row.forEach( ele1 => {
final[counter].push([])
col.forEach( ele2 => {
final[counter][ele1].push(array[ele1+i][ele2+j]);
})
})
counter+=1;
}
}
console.log(final)
You can loop through the array using map function and check for the index values to break into arrays as required.
let myArr =[[1, 2, 3, 4, 5, 6, 7, 8, 9],
[2, 3, 1, 5, 6, 4, 8, 9, 7],
[3, 1, 2, 6, 4, 5, 9, 7, 8],
[4, 5, 6, 7, 8, 9, 1, 2, 3],
[5, 6, 4, 8, 9, 7, 2, 3, 1],
[6, 4, 5, 9, 7, 8, 3, 1, 2],
[7, 8, 9, 1, 2, 3, 4, 5, 6],
[8, 9, 7, 2, 3, 1, 5, 6, 4],
[9, 7, 8, 3, 1, 2, 6, 4, 5]];
let newArr = convertTo3x3(myArr);
console.log(newArr);
function convertTo3x3(myArr){
let array3x3 = [];
myArr.map((row, rIndex) => {
let tempArr = [];
let row3 = [];
row.map((item, lIndex) => {
// convert each row to 3x3 rows
if(lIndex % 3 == 0){
// reset row3 for new 3x3 arr on every 1st item of 3x3
row3 = [];
}
row3.push(item);
if(lIndex % 3 == 2){
// push 3x3 row to tempArr on every 3rd item of 3x3
tempArr.push(row3);
}
});
array3x3.push(tempArr);
});
return array3x3;
}
Your code seems to work fine, but if you want a more javascriptey code, that performs worse, here you go.
let array = [
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[2, 3, 1, 5, 6, 4, 8, 9, 7],
[3, 1, 2, 6, 4, 5, 9, 7, 8],
[4, 5, 6, 7, 8, 9, 1, 2, 3],
[5, 6, 4, 8, 9, 7, 2, 3, 1],
[6, 4, 5, 9, 7, 8, 3, 1, 2],
[7, 8, 9, 1, 2, 3, 4, 5, 6],
[8, 9, 7, 2, 3, 1, 5, 6, 4],
[9, 7, 8, 3, 1, 2, 6, 4, 5],
];
let final = [];
array
.reduce((a, c) => {
const chunk = (arr, n) =>
arr.length ? [arr.slice(0, n), ...chunk(arr.slice(n), n)] : [];
return [...a, ...chunk(c, 3)];
}, [])
.map((el, i) => {
if (i % 3 == 0) {
final = [...final, [el]];
} else {
final[Math.floor(i / 3)] = [...final[Math.floor(i / 3)], el];
}
});
console.log(final);
Your code is trying to do too much. Make it difficult to understand.
I would first break it up. Write a simple function than knows how to take an array-of-arrays and return an arbitrary rectangular selection from it. Something like this, that's easy to test:
/**
* Snip a rectangular section of an array of arrays (ragged, 2D array).
* The returned array-of-arrys will ALWAYS be the specified size, padded
* with 'undefined' values to the specified size.
*
* #param {Object[][]} arr - An array of arrays (ragged 2D array)
* #param {number} row - Origin row: {row,col} denotes the upper-left corner of the rectangle to snip
* #param {number} col - Origin column: {row,col} denotes the upper-left corner of the rectangle to snip
* #param {number} nrows - Number of rows to snip
* #param {number} ncols - Nunber of columns to snip
*
* #returns {Object[][]}
*/
function snip(arr, row, col, nrows, ncols ) {
const selection = [];
for ( let r = row, i = 0 ; r < row+nrows ; ++r, ++i ) {
selection[i] = [];
const tmp = arr[r] ?? [];
for ( let c = col, j = 0 ; c < col+ncols ; ++c, ++j ) {
selection[i][j] = tmp[c];
}
}
return selection;
}
Once you have that, then chopping up your larger array-of-arrays into 3x3 arrays is easy.
This code starts at the top left corner or your 9x9 array and returns a flat list containing 9 separate 3x3 arrays, chopped out left-to-right and top-to-bottom:
final = [];
for ( let x = 0 ; x < 9; x += 3 ) {
for ( let y = 0 ; y < 9 ; y += 3 ) {
// (x,y) denotes the top left corner of the desired sub-array
final.push( snip(arr, x,y, 3,3 ) );
}
}
The nice thing about this approach is that it is easy to test, and
.
.
.
It's flexible. It can handle a source array of any size, and you can chop it up into subarrays of any size and in any order, whatever you see fit to do.
Before marking this as answered by another question please note this is an array of arrays, not a flat array, also, the number I have given are an example, I have just shown them so you can visually see what is happening.
I am trying to loop through an array of arrays.
I have the following array:-
[
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[3, 2, 1, 6, 5, 4, 9, 8, 7],
[6, 5, 4, 9, 8, 7, 3, 2, 1],
[7, 8, 9, 3, 2, 1, 6, 5, 4]
]
How is it possible to transform this array into chunks of 3x3 for example:-
[
[1, 2, 3, 1, 2, 3, 1, 2, 3],
[4, 5, 6, 4, 5, 6, 4, 5, 6],
[7, 8, 9, 7, 8, 9, 7, 8, 9],
[3, 2, 1, 6, 5, 4, 9, 8, 7],
[6, 5, 4, 9, 8, 7, 3, 2, 1],
[7, 8, 9, 3, 2, 1, 6, 5, 4],
]
As you can see from the array above I have chunked it using the first 3 values from each array and then by the 2nd 3n from each array and lastly the final 3n from each array.
So the array would be chunked like the following:-
1 2 3 | 4 5 6 | 7 8 9
1 2 3 | 4 5 6 | 7 8 9
1 2 3 | 4 5 6 | 7 8 9
---------------------
3 2 1 | 6 5 4 | 9 8 7
6 5 4 | 9 8 7 | 3 2 1
7 8 9 | 3 2 1 | 6 5 4
I have tried to loop through each line and resetting the column count when it hits an increment and increasing the row but this didn't work.
I can update the question with previous attempts if this is of any help?
Also just a note, the array will be different sizes but always divisible by a particular number, for the above example I have chosen the number 3.
I have updated the question with more information.
The array of arrays will always be divisible by a specific number, this example shows a divisible number of 3.
This can be solved with a chunk as per this question Split array into chunks combined with a zip as per this Javascript equivalent of Python's zip function
This has the benefit of being able to reverse the operation to get back to the original.
/** #see: https://stackoverflow.com/questions/8495687/split-array-into-chunks */
function chunk(array, chunk) {
let result = [];
for (let i = 0; i < array.length; i += chunk) {
result.push(array.slice(i, i + chunk));
}
return result;
}
/** #see: https://stackoverflow.com/questions/4856717/javascript-equivalent-of-pythons-zip-function */
function zip(...rows) {
return [...rows[0]].map((_, c) => rows.map((row) => row[c]));
}
const array = [
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[3, 2, 1, 6, 5, 4, 9, 8, 7],
[6, 5, 4, 9, 8, 7, 3, 2, 1],
[7, 8, 9, 3, 2, 1, 6, 5, 4],
];
const result = chunk(array, 3)
.flatMap((innerChunk) =>
zip(...innerChunk.map((arr) => chunk(arr, 3)))
.map((arr) => arr.flat())
);
console.log(result.map((a) => a.join(', ')));
// Allows the result to be reverted to the original
const reversed = chunk(result, 3)
.flatMap((innerChunk) =>
zip(...innerChunk.map((arr) => chunk(arr, 3)))
.map((arr) => arr.flat())
);
console.log(reversed.map((a) => a.join(', ')));
You can do it with a nested loop, slicing the array each time based on the size of the outer array.
const arr = [
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 5, 6, 7, 8, 9]
];
let out = [];
for(let i in arr) { out.push([]);}
arr.forEach((e, i) => {
let scale = e.length / arr.length;
for(let j in arr)
{
out[j] = out[j].concat(e.slice(j * scale, j * scale + scale));
}
});
console.log(out);
Here it is once more with your original array prior to your edit:
const arr = [
[1, 3, 2, 5, 2, 4, 3, 6, 8],
[1, 4, 3, 6, 7, 3, 6, 4, 5],
[2, 4, 1, 4, 6, 3, 7, 9, 7]
];
let out = [];
for(let i in arr) { out.push([]);}
arr.forEach((e, i) => {
let scale = e.length / arr.length;
for(let j in arr)
{
out[j] = out[j].concat(e.slice(j * scale, j * scale + scale));
}
});
console.log(out);
I don't understand this method of pop and unshift in this array
let nums = [1, 2, 3, 6, 9, 8, 7, 4];
const ids = [1, 2, 3, 6, 9, 8, 7, 4];
let btn5 = document.getElementById("btn5");
btn5.onclick = function() {
nums.unshift(nums.pop());
for (i = 0; i <= 7; i++) {
document.getElementById("btn" + ids[i]).innerHTML = nums[i];
}
}
nums.unshift(nums.pop()); is:
// Remove the last entry from the array
const tmp = nums.pop();
// Insert it at the beginning of the array
nums.unshift(tmp);
So for instance, the first time that runs, nums starts with:
[1, 2, 3, 6, 9, 8, 7, 4]
so pop removes the 4 from the end, and inserts it at the beginning:
[4, 1, 2, 3, 6, 9, 8, 7]
Live Example:
const nums = [1, 2, 3, 6, 9, 8, 7, 4];
console.log("before:", JSON.stringify(nums));
nums.unshift(nums.pop());
console.log("after: ", JSON.stringify(nums));
Details on MDN: pop, unshift.
Array pop method removes and return last element of an array. If you write something like,
let nums = [1, 2, 3, 6, 9, 8, 7, 4];
const ids = [1, 2, 3, 6, 9, 8, 7, 4];
const poppedValue = nums.pop(); //poppedValue = 4 and nums = [1, 2, 3, 6, 9, 8, 7]
And unshift method push the item at the beginning of the array.
nums.unshift(poppedValue); // nums = [4, 1, 2, 3, 6, 9, 8, 7];
I have an array of arrays
[
[1,3,5,7,8,8],
[1,3,5,7,8,8],
[1,3,5,7,8,8],
[1,3,5,7,8,8],
[1,3,5,7,8,8]
]
I am trying to insert a value between each item. So I have this:
let reelList = [
[1, 3, 5, 7, 8, 8],
[1, 3, 5, 7, 8, 8],
[1, 3, 5, 7, 8, 8],
[1, 3, 5, 7, 8, 8],
[1, 3, 5, 7, 8, 8]
]
reelList.map(reel => {
// Adds the separator (works)
let v = separate(reel, '-')
console.log(v)
return v
})
function separate(arr, value) {
return arr.reduce((result, element, index, array) => {
result.push(element)
index < array.length - 1 && result.push(value)
return result
}, []);
}
// Logs the new list to the console (doesn't work)
console.log(reelList)
When I log the values after I run the separate function they are separated, however, when I display reelList they are not separated. Why is that?
The map() function returns a new array. It doesn't modify the existing one.
You would need to set the results of reelList.map() to something else.
let reelList = [
[1, 3, 5, 7, 8, 8],
[1, 3, 5, 7, 8, 8],
[1, 3, 5, 7, 8, 8],
[1, 3, 5, 7, 8, 8],
[1, 3, 5, 7, 8, 8]
]
const finalResult = reelList.map(reel => {
// Adds the separator (works)
let v = separate(reel, '-')
console.log(v)
return v
})
function separate(arr, value) {
return arr.reduce((result, element, index, array) => {
result.push(element)
index < array.length - 1 && result.push(value)
return result
}, []);
}
// Logs the new list to the console (doesn't work)
console.log(finalResult);
If you want to edit the array in place, instead of using map(), use a forEach() with a callback that has a second and third parameter, which are index and array. Then you can update the array with the new values as you go.
let reelList = [
[1, 3, 5, 7, 8, 8],
[1, 3, 5, 7, 8, 8],
[1, 3, 5, 7, 8, 8],
[1, 3, 5, 7, 8, 8],
[1, 3, 5, 7, 8, 8]
]
reelList.forEach((reel, index, arr) => {
// Adds the separator (works)
let v = separate(reel, '-')
arr[index] = v;
console.log(v)
})
function separate(arr, value) {
return arr.reduce((result, element, index, array) => {
result.push(element)
index < array.length - 1 && result.push(value)
return result
}, []);
}
// Logs the new list to the console (doesn't work)
console.log(reelList);
You could map a new array with a calculated lenght and take the result of the calculated index ot the dash.
var array = [[1, 3, 5, 7, 8, 8], [1, 3, 5, 7, 8, 8], [1, 3, 5, 7, 8, 8], [1, 3, 5, 7, 8, 8], [1, 3, 5, 7, 8, 8]],
result = array.map(a => Array.from({ length: a.length * 2 - 1 }, (_, i) => a[i / 2] || '-'));
console.log(result);
I'm trying to change an item's index position in an array, but I cannot figure out a way.
{
"items": [
1,
3,
2
]
}
You can use splice to move an element in an array:
var arr = [
1,
3,
2
];
var oldIndex = 2,
newIndex = 1;
arr.splice(newIndex, 0, arr.splice(oldIndex, 1)[0]);
This makes [1, 2, 3]
The internal splice removes and returns the element, while the external one inserts it back.
Just for fun I defined a generic function able to move a slice, not just an element, and doing the computation of the index:
Object.defineProperty(Array.prototype, "move", {
value:function(oldIndex, newIndex, nbElements){
this.splice.apply(
this, [newIndex-nbElements*(newIndex>oldIndex), 0].concat(this.splice(oldIndex, nbElements))
);
}
});
var arr = [0, 1, 2, 7, 8, 3, 4, 5, 6, 9];
arr.move(5, 3, 4);
console.log('1:', arr) // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
var arr = [0, 1, 2, 7, 8, 3, 4, 5, 6, 9];
arr.move(3, 9, 2);
console.log('2:', arr); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
var arr = [0, 1, 2, 4, 5, 3, 6, 7];
arr.move(5, 3, 1);
console.log('3:', arr); // [0, 1, 2, 3, 4, 5, 6, 7]
var arr = [0, 3, 1, 2, 4, 5, 6, 7];
arr.move(1, 4, 1);
console.log('3:', arr); // [0, 1, 2, 3, 4, 5, 6, 7]
JS Bin
If you want to sort them in Unicode order (where numbers become strings) you can use sort() function.
items.sort();
If you have your custom order, you need to provide a sort function to the sort function.
function compare(a, b) {
if (a is less than b by some ordering criterion) {
return -1;
}
if (a is greater than b by the ordering criterion) {
return 1;
}
// a must be equal to b
return 0;
}
and you use it like this:
items.sort(compare(a, b));