I have a string "ABCDEFGHIJKLMN" that I need to shuffle in a specific manner. To do that, I write the characters sequentially in columns bottom -> top and then left -> right (4 chars per column for example) until all characters are done. If the last column is not complete, then the empty spaces need to be on the bottom (this is very important). Like so:
D H L N
C G K M
B F J
A E I
The shuffle is accomplished by producing a new string reading the block of letters as we read text, in rows left -> right:
"DHLNCGKMBFJAEI"
The cases where the columns are not complete (word.size % column_height !=0) complicate things considerably.
I came up with a few solutions, but I'm not sure if there is a simpler (ie, shorter OR easier to read) and more elegant way of coding this problem. My solutions either have an ugly, separate block of code to handle the final incomplete column or seem way too complicated.
My question is, could it be done better?
If you don't want any spoilers and decide to try and figure it out for yourself, stop reading now. If you want to work from what I fiddled so far, then a working piece of code is
var result = "";
var str = "ABCDEFGHIJKLMN";
var nr_rows = 4;
var current_row = 4;
var columns = Math.floor(str.length / nr_rows);
var modulus_table = str.length % nr_rows;
var modulus_position = -1;
for (var i = 0; i < nr_rows; i++) {
for (var j = 0; j < columns; j++) {
result += str[current_row + j * nr_rows - 1];
}
if (modulus_table > 0) {
result += str[str.length + modulus_position];
modulus_table--;
modulus_position--;
}
current_row--;
}
console.log(result);
Moving on to arrays, the next example would loop through each character, placing it correctly in a matrix-like array, but it doesn't work. The array needs to be created another way. For another example of this issue, see How to create empty 2d array in javascript?. This would also need an ugly hack to fix the last characters on the last incomplete column aligning to the bottom instead of the top.
var result = [[],[]];
var str = "ABCDEFGHIJKLMN";
var nr_rows = 4;
var row = nr_rows - 1;
var column = 0;
for (var i = 0; i < str.length; i++) {
result[row][column] = str[i];
row--;
if (row < 0) {
row = nr_rows;
column++;
}
}
console.log(result);
This last method goes full matrix array, but it quickly becomes complicated, since it needs to loop through the array in 3 different directions. First, create a dummy array with the characters in the wrong place, but where the 'undefined' positions correspond to those that should be left empty. That is acomplished by populating the array 'rotated 90ยบ' from the reading orientation.
Without this first step, the empty positions would be stacked at the bottom instead of the top.
A second pass is required to re-write the caracters in the correct places, skipping any holes in the matrix using the 'undefined' value. This check is made for every position and there is no separate block of code to handle an incomplete last line.
A third pass then reads every character in order to form the final shuffled string. All this seems way too complicated and confusing.
// matrix populated top->bottom and left->right
// with the characters in the wrong place
// but the undefined postions in the correct place of the empty positions
var matrix = [];
var str = "ABCDEFGHIJKLMN";
var rows = 4;
var columns = Math.ceil(str.length / rows);
var k = 0;
for (var i = 0; i < rows; i++) {
matrix[i] = [];
for (var j = columns - 1; j >= 0; j--) {
matrix[i][j] = str[k];
k++;
}
}
// populate the matrix with the chars in the correct place and the 'undefined' positions left empty
var k = 0;
for (var i = 0; i < rows; i++) {
for (var j = 0; j < columns; j++) {
if (matrix[i][j] != undefined) {
matrix[i][j] = str[k];
k++;
}
}
}
// read matrix in correct direction and send to string, skipping empty positions
var result = "";
for (var j = columns - 1; j >= 0; j--) {
for (var i = 0; i < rows; i++) {
if (matrix[i][j] != undefined) {
result += matrix[i][j];
}
}
}
console.log(result);
What if you just split/reverse the array into column groups, and convert to rows?
const result = str.match(/.{1,4}/g) // split string into groups of 4
.map(i => i.split('').reverse()) // reverse each group (bottom to top, and solves the last col issue)
.reduce((res, col) => { // reduce the groups into rows
col.forEach((c, i) => res[i] += c) // concat each string char to the right row
return res
}, ['','','','']) // initialise empty strings per row
.join('') // join the rows up
Fiddle here
If you wish to return a string, I don't see why any intermediate result should use an array when it doesn't have to. The following could use one less array, but it's convenient to use split and an array to control the while loop rather than mutate the string.
The idea is to fill the strings from the bottom up until the column is full, then keep adding from the bottom of each column until it runs out of characters to assign. The row to start filling from is based on how many characters are left and how many rows there are.
Rather than building strings, it could build arrays but then generating a string requires multiple joins.
It can also produce results where there are insufficient slots for all the characters, so a result using 9 characters from 10 or more using a 3x3 "matrix" (see last example).
function verticalShuffle(s, rows, cols) {
var result = [''];
s = s.split('');
while (s.length && result[0].length < cols) {
for (var i = (rows < s.length? rows : s.length) -1 ; i>=0; i--) {
if (!result[i]) result[i] = '';
result[i] += s.splice(0,1)[0] || '';
}
}
return result.join('');
}
var s = 'ABCDEFGHIJKLMN';
console.log(verticalShuffle(s, 4, 4)); // DHLNCGKMBFJAEI
console.log(verticalShuffle(s, 6, 3)); // FLNEKMDJCIBHAG
// Only use 9 characters
console.log(verticalShuffle(s, 3, 3)); // CFIBEHADG
This uses plain ed3 functionality that will run in any browser. I don't see the point of restricting it to ECMAScript 2015 or later hosts.
If interpret Question correctly, you can use for loop, String.prototype.slice() to to populate arrays with characters of string. Use Array.prototype.pop() within recursive function to get last element of array until each array .length is 0.
To create array
[
["D","H","L","N"],
["C","G","K","M"],
["B","F","J"],
["A","E","I"]
]
from vertically inverted string you can use for loop, String.prototype.slice() to set array of arrays containing elements having .length 4, or 3 once .length of parent array is 2, having been set with two arrays containing four elements
var str = "ABCDEFGHIJKLMN";
function fnVerticalInvert(str, arr, res) {
if (!arr && !res) {
arr = []; res = "";
}
if (str) {
for (var i = 0; i < str.length; i += 4) {
arr.push([].slice.call(str.slice(i, i + 4)));
}
}
for (var i = 0; i < arr.length; i++) {
if (arr[i].length) {
res += arr[i].pop()
}
}
if (arr.some(function(curr) {return curr.length}))
return fnVerticalInvert(null, arr, res);
for (var i = 0, l = 4, j = 0, n = l - 1, k; i < l; i++, j += l) {
if (i === l / 2) k = j;
arr[i] = [].slice.call(res.slice(!k ? j : k, !k ? j + l : k + n));
if (k) k += n;
}
return {str: res, arr:arr};
};
var res = fnVerticalInvert(str);
console.log(res);
Starting with this initial 2D array:
var initialArray = [[2,3],[6,7],[4,5],[1,2],[5,6],[2,3]];
I need to create this 3D array programmatically:
var fullArray = [
[[2,3],[6,7],[4,5],[1,2],[5,6],[2,3]],
[[3,4],[0,1],[5,6],[2,3],[6,7],[3,4]],
[[4,5],[1,2],[6,7],[3,4],[0,1],[4,5]],
[[5,6],[2,3],[0,1],[4,5],[1,2],[5,6]],
[[6,7],[3,4],[1,2],[5,6],[2,3],[6,7]],
[[0,1],[4,5],[2,3],[6,7],[3,4],[0,1]],
[[1,2],[5,6],[3,4],[0,1],[4,5],[1,2]],
[[2,3],[6,7],[4,5],[1,2],[5,6],[2,3]],
[[3,4],[0,1],[5,6],[2,3],[6,7],[3,4]],
[[4,5],[1,2],[6,7],[3,4],[0,1],[4,5]],
[[5,6],[2,3],[0,1],[4,5],[1,2],[5,6]]
];
See the pattern?
On each pair, the [0] position should increment to 6 (from any starting number <= 6) and then reset to 0 and then continue incrementing. Similarly, the [1] position should increment to 7 (from any starting number <= 7) and then reset to 1 and then continue incrementing.
In this example, there are 10 2D arrays contained in the fullArray. However, I need this number to be a variable. Something like this:
var numberOf2DArraysInFullArray = 12;
Furthermore, the initial array should be flexible so that initialArray values can be rearranged like this (but with the same iteration follow-through rules stated above):
var initialArray = [[6,7],[2,3],[5,6],[4,5],[1,2],[6,7]];
Any thoughts on how to programmatically create this structure?
Stumped on how to gracefully pull this off.
Feedback greatly appreciated!
Here's a solution, I've separated the methods, and I made it so if instead of pairs it's an N size array and you want the [2] to increase up to 8 and reset to 2, if that's not needed you can simplify the of the loop for(var j = 0; j < innerArray.length; j++)
var initialArray = [[2,3],[6,7],[4,5],[1,2],[5,6],[2,3]];
var create3DArray = function(array, size){
var newArray = [initialArray];
for(var i = 0; i < size; i++)
{
newArray.push(getNextArrayRow(newArray[i]));
}
return newArray;
}
var getNextArrayRow = function(array){
var nextRow = [];
for(var i = 0; i < array.length; i++)
{
var innerArray = array[i];
var nextElement = [];
for(var j = 0; j < innerArray.length; j++)
{
var value = (innerArray[j] + 1) % (7 + j);
value = value === 0 ? j : value;
nextElement.push(value);
}
nextRow.push(nextElement);
}
return nextRow;
}
console.log(create3DArray(initialArray,3));
Note, the results from running the snippet are a bit difficult to read...
var initialArray = [[2,3],[6,7],[4,5],[1,2],[5,6],[2,3]];
var numOfArrays = 10;
// get a range array [0, 1, 2, ...]
var range = [];
for (var i = 0; i < numOfArrays; i++) {
range.push(i);
}
var result = range.reduce(function(prev, index) {
if (index == 0) {
return prev;
}
prev.push(transformArray(prev[index - 1]));
return prev;
}, [initialArray])
console.log(result);
function transformArray(arr) {
return arr.map(transformSubArray)
}
function transformSubArray(arr) {
return arr.map(function(val) {
return val == 7 ? 0 : val + 1;
})
}
Here's a pretty simple functional-ish implementation
I've got an array that is constantly updating with analogue readings from an Arduino pin.
I'd like to create a function that takes the latest 100 values in the array, and returns an average of them (the array has a max length of 100,000 at which it starts 'shifting' and 'pushing' to make space for new values).
I created this function, but it returns 'NaN' every time:
function returnAverage(){
var averageArray = [];
var sum = 0;
var sampleEnd = values.length
for (var x = sampleEnd - 100; x < sampleEnd; x++) {
averageArray[x] = values[x]
}
for(var i = 0; i < averageArray.length; i++){
sum += parseInt(averageArray[i]);
}
var avg = sum/averageArray.length;
console.log(avg)
}
Any ideas?
If values is a array of numbers, last maximum 100 items average:
function returnAverage(values) {
var arr = values.slice(-100);
return arr.reduce(function(a, b){ return a + b; }, 0) / (arr.length || 1);
}
Issue number one is that the final value of sum and averageArray.lnegth is 0.
It seems this would happen because the "value" array is empty.
See example below:
var values = [0,1,2,3,4,5,6];
var averageArray = [];
var sum = 0;
var sampleEnd = values.length
for (var x = sampleEnd - 7; x < sampleEnd; x++) {
averageArray[x] = values[x]
}
for(var i = 0; i < averageArray.length; i++){
sum += parseInt(averageArray[i]);
}
var avg = sum/averageArray.length;
console.log(avg)
Edit: NaN is a result of division by zero. So you might want to check for that before calculating:
if(sum == 0 || averageArray.length == 0)
{
return 0;
}
I have an array of 100 random numbers between 1 and 49.
I would like to print out the array in rows of twelve elements, instead of printing the array in a single line.
Here is the code I have
<script type ="text/javascript">
var arr = [];
for (var i = 0, l = 100; i < l; i++) {
arr.push(Math.round(Math.random() * 49)+1)
}
document.write(arr);
document.write("\n");
</script>
I need to print the array in rows with 12 elements per row and also need to find the smallest element in the array.
You could try using splice:
while (arr.length > 0) {
document.write(arr.splice(0, 12))
}
However, after running that code the array will be []. If you don't want to modify the array, use slice instead:
for (var i = 0; i < arr.length; i += 12) {
document.write(arr.slice(i, i + 12))
}
This would be the conventional way of doing it. The array is reset however. Let us know more detail on your requirements.
var arr = [];
function getRandom( num ){
return Math.round(Math.random() * num)+1;
}
var counter = 0;
for (var i = 0; i < 100; i++) {
arr.push(getRandom( 49 ));
counter++;
if( counter >= 12 ){
document.write(arr);
document.write("<br/>");
arr = [];
counter = 0;
}
}