Transposing variable size array in javascript? - javascript

I am trying to get my 2D array to export as a .csv file. My array is in the form: [[1,2],[],[3],[4,5,6]]. I would like the spreadsheet to be formatted as
h1 h2 h3 h4
1 3 4
2 5
6
I cannot change the format I receive the data in. I've looked into ways to transpose the array but the solutions I've seen don't work with variable size arrays like mine. So far my only idea (in psuedoish code) is:
for (i < length of longest column; i++) {
var arr = [];
if (i > col1.length) arr.push("");
else arr.push(col1[i]);
//so on for rest of cols
csvFile += arr;
}
Is there a better way to do it?

You could transpose the array by using arrays with the length of the outer array as length for every line.
var array = [[1, 2], [], [3], [4, 5, 6], [,,7]],
result = array.reduce((r, a, i, { length }) => {
a.forEach((v, j) => {
r[j] = r[j] || new Array(length).fill('');
r[j][i] = v;
});
return r;
}, []);
console.log(result);

You are almost there, just missing the inner loop (because a CSV table has rows X columns):
var input = [[1,2],[],[3],[4,5,6]];
var lengthOfLongestColumn = Math.max(...input.map(a => a.length));
var csvFile = [['h1','h2','h3','h4']];
for (var i = 0; i < lengthOfLongestColumn; i++) {
var row = [];
for (var j = 0; j < input.length; j++) {
var col = input[j];
if (i >= col.length) {
row.push('');
}
else {
row.push(col[i]);
}
}
csvFile.push(row);
}
console.log(csvFile)

Related

Push duplicate items into a separate array in Javascript with for loop?

For some reason, the manipulated doubleArray below is not shown in the console. Any variables that I declare after the for loop won't show to the console on both cases. Consider that in the first algorithm, there is only one for loop with x being incremented everytime. Whereas, in the second algorithm, it's a nested for loop. Can someone help me fix my error in both algorithms?
First Algorithm:
var isDuplicate = function() {
var helloWorld = [1,2,3,4,3];
var doubleValue = [];
var x = 0;
for (i = 0; i < helloWorld.length; i++) {
x = x + 1;
if (helloWorld[i] === helloWorld[x] && i !== x) {
doubleValue.push(helloWorld[i])
console.log(helloWorld[i]);
} else {
continue;
}
}
console.log(doubleValue);
};
The second Algorithm:
var isDuplicate = function() {
var helloWorld = [1,2,3,4,3];
var doubleValue = [];
for (i = 0; i < helloWorld.length; i++) {
for (x = 1; x < helloWorld.length; i++) {
if (helloWorld[i] === helloWorld[x] && i !== x) {
doubleValue.push(helloWorld[x]);
}
}
}
console.log(doubleValue);
};
In first algorithm, you are only checking if the number at current index is equal to the number at the next index, meaning you are only comparing numbers at consecutive indexes. First algorithm will work only if you have duplicate numbers on consecutive indexes.
In second algorithm, you are incrementing i in both loops, increment x in nested loop, change x = 1 to x = i + 1 and your error will be fixed.
Here's the fixed second code snippet
var isDuplicate = function() {
var helloWorld = [1,2,3,4,3, 1, 2];
var doubleValue = [];
for (let i = 0; i < helloWorld.length; i++) {
for (let x = i + 1; x < helloWorld.length; x++) {
if (helloWorld[i] === helloWorld[x] && i !== x) {
doubleValue.push(helloWorld[x]);
}
}
}
console.log(doubleValue);
};
isDuplicate();
Heres's another way to find the duplicates in an array, using an object. Loop over the array, if current number is present as key in the object, push the current number in the doubleValue array otherwise add the current number as key-value pair in the object.
const isDuplicate = function() {
const helloWorld = [1,2,3,4,3, 1, 2];
const doubleValue = [];
const obj = {};
helloWorld.forEach(n => obj[n] ? doubleValue.push(n): obj[n] = n);
console.log(doubleValue);
};
isDuplicate();
Not entirely sure what you are trying to do. If you are only looking for a method to remove duplicates you can do the following:
const hello_world = [1, 2, 2, 3, 4, 5, 5];
const duplicates_removed = Array.from(new Set(hello_world));
A set is a data object that only allows you to store unique values so, when converting an array to a set it will automatically remove all duplicate values. In the example above we are creating a set from hello_world and converting it back to an array.
If you are looking for a function that can identify all the duplicates in an array you can try the following:
const hello_world = [1, 2, 2, 3, 4, 5, 5];
const duplicates_found = hello_world.filter((item, index) => hello_world.indexOf(item) != index);
The main problem by finding duplicates is to have nested loop to compare each element of the array with any other element exept the element at the same position.
By using the second algorithm, you can iterate from the known position to reduce the iteration count.
var isDuplicate = function(array) {
var doubleValue = [];
outer: for (var i = 0; i < array.length - 1; i++) { // add label,
// declare variable i
// no need to check last element
for (var j = i + 1; j < array.length; j++) { // start from i + 1,
// increment j
if (array[i] === array[j]) { // compare values, not indices
doubleValue.push(array[i]);
continue outer; // prevent looping
}
}
}
return doubleValue;
};
console.log(isDuplicate([1, 2, 3, 4, 3])); // [3]
You could take an object for storing seen values and use a single loop for getting duplicate values.
const
getDuplicates = array => {
const
seen = {}
duplicates = [];
for (let value of array) {
if (seen[value]) duplicates.push(value);
else seen[value] = true;
}
return duplicates;
};
console.log(getDuplicates([1, 2, 3, 4, 3])); // [3]
Your first algorithm doesn't work because it only looks for duplicates next to each other. You can fix it by first sorting the array, then finding the duplicates. You can also remove the x and replace it by ++i in the loop.
var isDuplicate = function() {
var helloWorld = [1,2,3,4,3,6];
var doubleValue = [];
helloWorld = helloWorld.sort((a, b) => { return a - b });
for (i = 0; i < helloWorld.length; i++) {
if (helloWorld[i] === helloWorld[++i]) {
doubleValue.push(helloWorld[i])
console.log(helloWorld[i]);
} else {
continue;
}
}
console.log(doubleValue);
};
isDuplicate();
For the second algorithm loop, you probably meant x++ instead of i++ in the second loop. This would fix the problem.
var isDuplicate = function() {
var helloWorld = [1,2,3,4,3,4];
var doubleValue = [];
for (i = 0; i < helloWorld.length; i++) {
for (x = i + 1; x < helloWorld.length; x++) {
if (helloWorld[i] === helloWorld[x]) {
doubleValue.push(helloWorld[x]);
}
}
}
console.log(doubleValue);
};
isDuplicate()
The first algorithm can't be fixed, it can only detect consecutive duplicates,
in the second algorithm you increment i in both loops.
To avoid the duplicates beeing listed too often, you should start the second loop with i + 1

Permutation with max length parameter

I'm writing an function to create all possible permutation with max length limitation, and each item in array can only be used once.
The original permutation code as follows:
function permutations(list)
{
// Empty list has one permutation
if (list.length == 0)
return [[]];
let result = [];
for (let i=0; i<list.length; i++)
{
// Clone list (kind of)
let copy = Object.create(list);
// Cut one element from list
let head = copy.splice(i, 1);
// Permute rest of list
let rest = permutations(copy);
// Add head to each permutation of rest of list
for (let j=0; j<rest.length; j++)
{
let next = head.concat(rest[j]);
result.push(next);
}
}
return result;
}
How best to add this max length parameter to create unique combination result? I added maxLength. But in recursion stuck on where to best implement this parameter.
From a minimalist point of view, stop the recursion not when you have shrinked list from its size but when maxLength element have been taken from the list.
function permutations(list, maxLength)
{
// Empty list has one permutation
if (maxLength == 0)
return [[]];
let result = [];
for (let i=0; i<list.length; i++)
{
// Clone list (kind of)
let copy = Object.create(list);
// Cut one element from list
let head = copy.splice(i, 1);
// Permute rest of list
let rest = permutations(copy, maxLength-1);
// Add head to each permutation of rest of list
for (let j=0; j<rest.length; j++)
{
let next = head.concat(rest[j]);
result.push(next);
}
}
return result;
}
const maxLength = 4
console.time('by cp')
var r = permutations([1, 2, 3, 4, 5, 6], maxLength)
console.timeEnd('by cp')
console.log(r)
However, a similar approach but slightly more performant is to avoid copying the array for every "try" and only copy it when a solution is found
function permutations2(iList, maxLength)
{
const cur = Array(maxLength)
const results = []
function rec(list, depth = 0) {
if (depth == maxLength) {
return results.push(cur.slice(0))
}
for (let i = 0; i < list.length; ++i) {
cur[depth] = list.splice(i, 1)[0]
rec(list, depth + 1)
list.splice(i, 0, cur[depth])
}
}
rec(iList)
return results;
}
console.time('cp on solution')
var r = permutations2([1, 2, 3, 4, 5, 6], 4)
console.timeEnd('cp on solution')
console.log(r)

Compare Arrays with Javascript and build another Array

In Javascript:
I have an existing array like [4,5,6,10] - (These are 'repid').
I have an ajax response like [{"repid":5,"avgAmount":2.5},{"salesrepid":10,"avgAmount":3.0}].
I have to build a third array which will compare the 'repids' of the 2 arrays and build a third array so that it will place a '0' if the repids do not match or else the 'avgAmount' if they match.
So, in my case above, I would 'build' a third array:
[0, 2.5, 0, 3.0]
I've tried many variances of:
//need to assign the sales average values to the proper repid
for (var i = 0; i < repIds.length; i++) {
for (var j = 0; j < salesrepids.length; j++) {
if (repIds[i] == salesrepids[j]) {
salesvalues.push(key.avgAmount);
} else { salesvalues.push("0"); }
};
}
}
}
You need to address the correct keys of your objects. And also only add the 0 in case you don't find any matching entry:
var repIds = [4, 5, 6, 10];
var salesrepids = [{"repid": 5, "avgAmount": 2.5}, {"repid": 10, "avgAmount": 3.0}]
var salesvalues = [];
for (var i = 0; i < repIds.length; i++) {
var noMatch = true;
for (var j = 0; j < salesrepids.length; j++) {
if (repIds[i] === salesrepids[j]['repid']) {
salesvalues.push(salesrepids[j]['avgAmount']);
noMatch = false;
}
}
if (noMatch) {
salesvalues.push(0);
}
}
console.log(salesvalues);
You can do something like using map and find:
Loop through the first array -> Check if the id exists in the second array using find -> If yes, return it's avgAmount else return 0.
const ids = [4,5,6,10],
amounts = [{"repid":5,"avgAmount":2.5},{"repid":10,"avgAmount":3.0}];
const output = ids.map(i => {
const found = amounts.find(a => a.repid === i);
return found ? found.avgAmount : 0;
})
console.log(output)
May be like this:
var repids = [4,5,6,10];
var returns = [{"repid":5,"avgAmount":2.5},{"salesrepid":10,"avgAmount":3.0}];
var results = [];
for(var key in returns){
if(repids.includes(returns[key].repid)){
results.push(returns[key].repid);
results.push(returns[key].avgAmount);
}
if(repids.includes(returns[key].salesrepid)){
results.push(returns[key].salesrepid);
results.push(returns[key].avgAmount);
}
}
console.log(results);

Re-order array in javascript

Say you have an array like this and I want to reorder it.
var myarray = [
[178, 559, 906, 1252] , [381 , 537 , 937 , 1115] , [346 , 529 , 913 , 1069]
];
What I wanted to do was to loop through each of the arrays, take the 1st value of each array on the first loop and push each into a separate array. The second time the loop runs, take the 2nd value of each array, push those into the separate array and so on and so on. So that the separate array looks like this;
var myNewArray = [178, 381, 346, 559, 537, 529, 906, 937, 913, 1252, 1115, 1069];
I've got so far as to loop through each array and get all of the values, but can't work out the logic to target ONLY the 1st values on the first loop, the 2nd of the second loop etc etc
var arraylength = myarray.length;
for (var i = 0; i < arraylength; i++ ) {
console.log(i+1 + " time around");
var noc = myarray[i].length;
for (var k = 0; k < noc; k++) {
var a = myarray[i][k];
console.log(a);
};
};
Here's a JSFiddle
Okay...
Perhaps this is overboard, but I had a long trip home and some time to kill.
Your algorithm troubles revolve around the fact that you're missing a step.
What you actually need to do is loop through the range of the longest array.
Which means that you need to create a range (an actual range, or just know the min/max bounds of it) that goes from 0 to the max of all of the lengths of all of the arrays.
When you've done that, you need to go through that range, and within that range, you need to go through the list of all arrays (looping through each 2nd-dimension array, per iteration).
For each array, you check if it has an element at the current index.
If it does, add it to the new array.
The first step (the one you're missing) is almost like dealing cards; you have 4 people at the table, but it's actually the 52 cards that you're iterating through on the outside, not the 4 people.
This has a bunch of different names, depending on what you're doing.
This might be a zip a merge a rotation (though rotation doesn't really account for the flattening, just the shuffling).
So without further ado, here are 3 solutions, which are all different takes on this.
The first solution is the more-classical "JavaScript as Java" implementation:
function findMax (arrays) {
var i = 0;
var l = arrays.length;
var max = 0;
var array = [];
for (; i < l; i += 1) {
array = arrays[i];
max = array.length > max ? array.length : max;
}
return max;
}
function rotateAndFlatten (arrays) {
var flattenedArray = [];
var maxLength = findMax(arrays);
var inner = 0;
var outer = 0;
var array;
var currentValue;
for (; outer < maxLength; outer += 1) {
for (inner = 0; inner < arrays.length; inner += 1) {
array = arrays[inner];
currentValue = array[outer];
if (currentValue || currentValue === 0) {
flattenedArray.push(currentValue);
}
}
}
return flattenedArray;
}
var inputArray = [ [1, 2, 3], [4, 5, 6, 7], [8, 9, 10] ];
var outputArray = rotateAndFlatten(inputArray);
document.querySelector(".ResultInput--ES3").textContent = JSON.stringify(inputArray);
document.querySelector(".ResultOutput--ES3").value = JSON.stringify(outputArray);
<div ><pre>Input: <code class="ResultInput ResultInput--ES3"></code></pre></div>
<div ><pre>Output: <code ><output class="ResultOutput ResultOutput--ES3"></output></code></pre></div>
The second is the ES5 way I'm more used to thinking, these days, with partially-applied functions, and working with sets of things one operation at a time, rather than instances of things with manual loop management:
function makeRange (min, max) {
var range = [];
var i = min;
while (i < max) {
range.push(i);
i += 1;
}
return range;
}
function concat (a, b) {
return a.concat(b);
}
function identity (x) {
return x;
}
function max (a, b) {
return b > a ? b : a;
}
function pluck (key) {
return function pluckFrom (obj) {
return obj[key];
};
}
function fillIndexArrays (arrays) {
return function (i) {
return arrays.map(pluck(i));
};
}
function rotateAndFlatten (array) {
var getLength = pluck("length");
var maxLength = array.map(getLength).reduce(max, 0);
var indices = makeRange(0, maxLength);
return indices.map(fillIndexArrays(array)).reduce(concat, []).filter(identity);
}
var inputArray = [ [1, 2, 3], [4, 5, 6, 7], [8, 9, 10] ];
var outputArray = rotateAndFlatten(inputArray);
document.querySelector(".ResultInput--ES5").textContent = JSON.stringify(inputArray);
document.querySelector(".ResultOutput--ES5").value = JSON.stringify(outputArray);
<div ><pre>Input: <code class="ResultInput ResultInput--ES5"></code></pre></div>
<div ><pre>Output: <code ><output class="ResultOutput ResultOutput--ES5"></output></code></pre></div>
And here's the ES6 version of that, which can now use generators and splat operators to greatly simplify the construction of a range, and use lambdas to where code might be compacted and be equally legible (to me/my team):
const max = (a, b) => b > a ? b : a;
const identity = x => x;
const concat = (a, b) => a.concat(b);
function * range (min, max) {
let i = min;
while (i <= max) {
yield i;
i += 1;
}
};
const pluck = (key) => { return (obj) => obj[key]; };
function rotateAndFlatten (arrays) {
const getLength = pluck("length");
const maxLength = arrays.map(getLength).reduce(max, 0);
const indices = [...range(0, maxLength)];
return indices
.map(i => arrays.map(pluck(i)))
.reduce(concat, [])
.filter(identity);
}
var inputArray = [ [1, 2, 3], [4, 5, 6, 7], [8, 9, 10] ];
var outputArray = rotateAndFlatten(inputArray);
document.querySelector(".ResultInput--ES6").textContent = JSON.stringify(inputArray);
document.querySelector(".ResultOutput--ES6").value = JSON.stringify(outputArray);
<div ><pre>Input: <code class="ResultInput ResultInput--ES6"></code></pre></div>
<div ><pre>Output: <code ><output class="ResultOutput ResultOutput--ES6"></output></code></pre></div>
As a bonus, here's how I might implement it if I were writing JS as though I were writing C code that makes me very sad when I have to debug a logic error (but cuts right to the quick of the algorithm):
function init (arrs) {
var max;
var i = 0;
var l = arrs.length;
var max = 0;
for (i = 0; i < l; i++)
if (max < arrs[i].length)
max = arrs[i].length;
var j = 0;
var arr = [];
for (i = 0; i < max; i++)
for(j = 0; j < arrs.length; j++)
if (arrs[j][i] !== undefined)
arr.push(arrs[j][i]);
document.querySelector(".ResultOutput--C").value = JSON.stringify(arr);
}
var arrs = [ [1, 2, 3], [4, 5, 6, 7], [8, 9, 10] ];
document.querySelector(".ResultInput--C").textContent = JSON.stringify(arrs);
init(arrs);
<div ><pre>Input: <code class="ResultInput ResultInput--C"></code></pre></div>
<div ><pre>Output: <code ><output class="ResultOutput ResultOutput--C"></output></code></pre></div>
Hope that gives you something to chew upon, and some insight into the low-level algorithms which might be at play, and the higher-level ways of implementing those lower-level algorithms.
Alternatively you can do it like this: .shift() returns the first item of the array, but note that this solution will alter the original array
var myarray = [
[178, 559, 906, 1252],
[381, 537, 937, 1115],
[346, 529, 913, 1069]
];
var resultArray = [];
var maxlen = Math.max.apply(null, myarray.map(function(i) {
return i.length; }));
var l = 0;
while (l < maxlen) {
var s = 0;
var a = [];
for (i = 0; i < myarray.length; i++) {
a.push(myarray[i].shift());
}
resultArray.push(a);
l++;
s++;
}
console.log(resultArray);
Instead of iterating myarray in the outer loop, and each subarray in the inner one, you should do the opposite. Then it's a bit tricky, because the outer loop iterates the subarrays, but they could have different lengths. I added a done variable to know where to stop.
var myNewArray = [];
for(var i=0, done=false; !done; ++i) {
done = true;
for(var j=0; j<myarray.length; ++j) {
if(i < myarray[j].length) {
done = false;
myNewArray.push(myarray[j][i]);
}
}
}
Lodash has a method called zip that does what you are trying to do.
console.log(_.zip(myarray)); // [[178, 381, 346], [559, 537, 529], [906, 937, 913], [1252, 1115, 1069]]

Javascript: assigning data to Multidimensional array

I am trying to convert Java code to Javascript and I am trying to assign data to 3 dimensional array and I am getting "TypeError: can't convert undefined to object " error. Following is my code. Thanks in advance for any help.
var initData = [[2], [12], [2]];
for (var i = 0; i < 12; i++)
{
initData[0][i][0] = -1;
initData[0][i][1] = -1;
initData[1][i][0] = -1;
initData[1][i][1] = -1;
}
[[2], [12], [2]];
That's not a declaration of dimensions, that's four array literals. There are no multidimensional arrays in JS. They're just one-dimensional lists that can contain arbitrary values (including other lists).
To create and fill an array that contains other arrays you have to use the following:
var initData = []; // an empty array
for (var i = 0; i < 2; i++) {
initData[i] = []; // that is filled with arrays
for (var j = 0; j < 12; j++) {
initData[i][j] = []; // which are filled with arrays
for (var k = 0; k < 2; k++) {
initData[i][j][k] = -1; // which are filled with numbers
}
}
}
or, to apply your loop unrolling:
var initData = [[], []]; // an array consisting of two arrays
for (var i = 0; i < 12; i++) {
// which each are filled with arrays that consist of two numbers
initData[0][i] = [-1, -1];
initData[1][i] = [-1, -1];
}
initData is a list of three lists, [2], [12] and [2]. Each one with one element.
In order to init a list(or array), you must do
var initData = [];
Then store in initData another list, like initData[0] = [] and so on... like it's mentioned, arrays/lists in javascript aren't initialized with a limit size.

Categories

Resources