How to convert multiple arrays to CSV columns using JavaScript? - javascript

Goal: Convert multiple JavaScript arrays to CSV columns.
Context: Using an API to scan through a fleet of assets with GPS units and comprising a list of which assets are inactive, among other metrics.
Problem: The part I need assistance with is the algorithm in my nested for-loop that determines the index to insert the cell at the correct row/column.
I have looked at many packages on NPM but it doesn't appear that I was able to easily convert multiple arrays to CSV columns.
I haven't needed to ask a question here yet so it's certainly possibly I am going about this the wrong way or missed a solution to this; so any help or hints to achieve the goal are highly appreciated.
The running code is below, but the desired output would be:
All Assets,Idle Assets
1000,2001,
1001,2002,
1002,2003,
1003,2004,
1004,,
Rather than:
All Assets,Idle Assets
1000,2001,
2002,
2003,
2004,
,
1001,1002,1003,1004,
//Sample Array Data (can handle more arrays/columns)
const allAssets = ['1000', '1001', '1002', '1003', '1004'];
const idleAssets = ['2001', '2002', '2003', '2004'];
////Arrays to CSV columns
const headers = ['All Assets', 'Idle Assets'];
const columns = [allAssets, idleAssets];
//Identify columnLen (# of arrays) & rowLen (length of longest array)
let longestArr = 0;
columns.forEach((arr) =>
arr.length > longestArr
? longestArr = arr.length
: undefined);
const rowLen = longestArr;
const columnLen = columns.length;
const heading = (headers.join(',')).concat('\n');
const csvConstructor = () => {
const data = [];
for (let column = 0; column < columnLen; column++) {
const columnArray = columns[column];
for (let row = 0; row < rowLen; row++) {
let cell;
//Account for different sized arrays
columnArray[row] === undefined
? cell = ','
: cell = columnArray[row].concat(',');
//New line for cells in last column
(columns.length - 1) === column
? cell = cell.concat('\n')
: undefined;
//Dynamically insert cell into appropriate data index
const insertAt = column + row; //<--- Need help here
data.splice(insertAt, 0, cell);
}
}
const result = heading.concat(data.join(''));
return result
}
const csv = csvConstructor();
console.log(csv);

Your csvConstructor() is iterating columns individually, but for a table, you need to iterate columns in parallel. Also, it is trying to juggle two different concerns at the same time (transforming the data, and constructing the csv string), so it is a bit difficult to follow.
The problem is logically split into two phases. In the first phase, you want a well-formatted array that reflects the CSV's tabular structure (or if it's a large data set, then you probably want an iterator that yields rows one by one, but I won't do that version here).
The output should be in this format:
const result = [
[header A, header B],
[value A, value B],
[value A, value B],
// ...etc...
]
Once we have that structure, we can transform it into CSV.
So to get that based on your data:
function toCsvRows(headers, columns) {
const output = [headers]
const numRows = columns.map(col => col.length)
.reduce((a, b) => Math.max(a, b))
for (let row = 0; row < numRows; row++) {
output.push(columns.map(c => c[row] || ''))
}
return output
}
function toCsvString(data) {
let output = ''
data.forEach(row => output += row.join(',') + '\n')
return output
}
function csvConstructor(headers, columns) {
return toCsvString(toCsvRows(headers, columns))
}
Here's a working fiddle:
https://jsfiddle.net/foxbunny/fdxp3bL5/
EDIT: Actually, let me do the memory-efficient version as well:
function *toCsvRows(headers, columns) {
yield headers
const numRows = columns.map(col => col.length)
.reduce((a, b) => Math.max(a, b))
for (let row = 0; row < numRows; row++) {
yield columns.map(c => c[row] || '')
}
}
function toCsvString(data) {
let output = ''
for (let row of data) {
output += row.join(',') + '\n'
}
return output
}
function csvConstructor(headers, columns) {
return toCsvString(toCsvRows(headers, columns))
}

If you want a simple way using zip two arrays with comma. then join header and zipped rows with \n.
const allAssets = ['1000', '1001', '1002', '1003', '1004'],
idleAssets = ['2001', '2002', '2003', '2004'],
headers = ['All Assets', 'Idle Assets'],
zip = (a, b) => a.map((k, i) => `${k}, ${b[i]||''}`),
zipLonger = (a, b) => a.length > b.length ? zip(a, b) : zip(b, a);
console.log(headers.join(', ')+'\n'+
zipLonger(allAssets, idleAssets).join('\n'))
Result:
All Assets, Idle Assets
1000, 2001
1001, 2002
1002, 2003
1003, 2004
1004,

Related

How to find all no repeat combinations form specific single arrays use Javascript

I am a Javascript beginner, I have a personal project to use program to find all the possible & no repeat combinations form specific arrays
I suppose have 3 sets of products and 10 style in each set, like this array
[1,2,3,4..10,1,2,4...8,9,10]
①②③④⑤⑥⑦⑧⑨⑩
①②③④⑤⑥⑦⑧⑨⑩
①②③④⑤⑥⑦⑧⑨⑩
totaly array length = 30
I plan to randomly separate it into 5 children, but they can't repeat the same products style
OK result:
①②③④⑤⑥ ✔
②③④⑤⑥⑦ ✔
①②③⑧⑨⑩ ✔
④⑥⑦⑧⑨⑩ ✔
①⑤⑦⑧⑨⑩ ✔
Everyone can evenly assign products that are not duplicated
NG:
①②③④⑤⑥ ✔
①②③④⑤⑦ ✔
①②⑥⑧⑨⑩ ✔
③④⑤⑦⑧⑨ ✔
⑥⑦⑧⑨⑩⑩ ✘ (because number 10 repeated)
My solution is randomly to assign 5 sets of arrays, then use "new Set(myArray[i]).size;" check the value sum is 30 or not, Use [do..white], while sum is not 30 then repeat to do the random assign function until the result is not duplicated.
like this:
function splitArray() {
do {
var productArray = [1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10]; //Just for sample, actualy this array will be more then 30
var productPerChildArray = [];
for (var i = 0; i < 5; i++) {
GroupNum = [];
for (var v = 0; v < 6; v++) {
var selectNum = productArray[Math.floor(Math.random() * productArray.length)];
GroupNum.push(selectNum);
productArray = removeItemOnce(productArray, selectNum);
}
productPerChildArray.push(GroupNum);
}
} while (checkIfArrayIsUnique(productPerChildArray));
return productPerChildArray;
}
//---------check repeat or not----------
function checkIfArrayIsUnique(myArray) {
var countRight = 0;
for (var i = 0; i < myArray.length; i++) {
countRight += new Set(myArray[i]).size;
}
return (countRight != 5*6);
}
//----------update productArray status----------
function removeItemOnce(arr, value) {
var index = arr.indexOf(value);
if (index > -1) {
arr.splice(index, 1);
}
return arr;
}
console.log(splitArray());
Seems to solve the problem, but actualy productArray is not must 30, this solution will spend to much time to try the no repeat combination. Low efficiency
I believe they have a other solution to solve the problem better than my idea
Any help would be greatly appreciated.
My approach would be: just place the next number in an array that is selected randomly - of course, filter out those that already contain the next number.
// the beginning dataset
const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// the size of the groups to be formed (must divide the
// size of beginning dataset without a remainder)
const ARR_LENGTH = 6
// creating the array that will be filled randomly
const getBaseArray = ({ data, arrLength }) => {
const num = parseInt(data.length / arrLength, 10)
return Array.from(Array(num), () => [])
}
// filter arrays that satisfy conditions
const conditions = ({ subArr, val }) => {
if (subArr.includes(val)) return false
if (ARR_LENGTH <= subArr.length) return false
return true
}
const getArraysFiltered = ({ val, arr }) => {
return arr
.map((e, i) => ({
origIdx: i,
subArr: e,
}))
.filter(({ subArr }) => conditions({ subArr, val }))
}
// select a random array from a list of arrays
const getRandomArrIdx = ({ arr }) => {
return Math.floor(Math.random() * arr.length)
}
// get the original array index from the filtered values
const getArrIdx = ({ val, arr }) => {
const filtered = getArraysFiltered({ val, arr })
if (!filtered.length) return -1
const randomArrIdx = getRandomArrIdx({ arr: filtered })
return filtered[randomArrIdx].origIdx
}
const getFinalArr = ({ data }) => {
// short circuit: if the data cannot be placed in
// arrays evenly, then it's a mistake (based on
// the current ruleset)
if (data.length % ARR_LENGTH) return [false]
// creating the array that will hold the numbers
const arr = getBaseArray({ data, arrLength: ARR_LENGTH })
let i = 0;
for (i; i < data.length; i++) {
const idx = getArrIdx({
val: data[i],
arr,
})
// if there's no place that the next number could be
// placed, then break (prematurely), so the algorithm
// can be restarted as early as possible
if (idx === -1) break;
arr[idx].push(data[i])
}
if (i < data.length) {
// restart algorithm if we couldn't place
// all the numbers in the dataset
return getFinalArr({ data })
} else {
return arr
}
}
// constructing the final array of arrays & logging them:
console.log('res', getFinalArr({ data }).join(' '))
console.log('res', getFinalArr({ data }).join(' '))
console.log('res', getFinalArr({ data }).join(' '))
console.log('res', getFinalArr({ data }).join(' '))
I don't know if it's more efficient, but I found this question interesting.
Now, the algorithm is
broken down into small logical steps that are
easy to follow
simple to tweak to needs
works with all sizes of data (if the groups to be made can be filled equally)

All variations of matrix

I am trying to write an algorithm for getting all possible variations of a matrix in JavaScript.
Here is what I want to achieve:
blue,red
male,female,other
small,medium,large
-----------------
blue,male,small
blue,male,medium
blue,male,large
blue,female,small,
blue,female,medium,
blue,female,large
blue,other,small,
blue,other,medium,
blue,other,large
red,male,small
red,male,medium
red,male,large
red,female,small,
red,female,medium,
red,female,large
red,other,small,
red,other,medium,
red,other,large
Any idea how this can be done?
Wht you want is called the Cartesian product of several lists. If you have a fixed set of lists, nested loops are an easy way to generate the Cartesian product.
You can generalize this for an arbitrary list by iterating through the lists in odometer style. (Each digit of the odometer may have a different range, though.)
Here's how:
function cartesian(m) {
const res = [];
const index = []; // current index
const max = []; // length of sublists
for (let i = 0; i < m.length; i++) {
index.push(0);
max.push(m[i].length);
}
for (;;) {
res.push(index.map((i, j) => m[j][i]));
let i = 0;
index[i]++;
while (index[i] == max[i]) {
index[i] = 0;
i++;
if (i == m.length) return res;
index[i]++;
}
}
}
Call it like this:
const m = [
["blue", "red"],
["male", "female", "other"],
["small", "medium", "large"],
];
const p = cartesian(m);
This creates one huge list of all possibilities, which probably isn't ideal. You can change the function by doing whatever you want to do with each possibilitiy where the code above pushes to current list to the result array. The code above changes the first item in each iteration, which is the opposite of what you show in your post.
You can iterate through arrays using reduce:
let data = ['blue','red']
let data2 =['male','female','other']
let data3 =['small','medium','large']
let result = data.reduce((acc,rec) => {
return acc.concat(data2.reduce((acc2, rec2) => {
return acc2.concat(data3.reduce((acc3,rec3) =>{
return acc3.concat([`${rec}, ${rec2}, ${rec3}`])
},[]))
},[]))
},[])
console.log(result)

Selecting values from an arry and storing in a variable

I am trying to return the second and third value in an array - but struggling to do so with my limited JS knowledge.
I have two arrays which are combined into one and looks like this:
"0.003839795 - clothes ,0.171756425 - food",0.00741361072561247 - electronics"
I want to order the array by highest score, and then pull back the score and the category into separate variables.
category1 = value
category1score = value
cat2 score = value
cat2 score = value
The script below works and can return the top product name, but I can't work out how to make it return the associated score or the second and third prod/scores in the array...
var product;
var finalString=","+user.get('array1')+user.get('array2');
var finalArray = finalString.split(',');
product = finalArray.sort()[finalArray.length - 1] +' - ';
return product.split(' - ')[1];
The output for above array would look like:
cat1 = food
cat1score = 0.171756425
cat2 = electronics
cat2score = 0.00741361072561247
cat3= clothes
cat3score = 0.003839795
Here is an approach that uses a temporary array of objects, and sorts by a field in the object. The parsing provides some flexibility for optional spaces. It also converts the score to a float to ensure leading zeros, negative signs, etc, do not cause inaccurate ordering.
const data = `0.003839795 - clothes ,0.171756425 - food ,0.00741361072561247 - electronics`
let items = [];
const parts = data.split(/\s*,\s*/)
parts.forEach(function (part) {
const pair = part.split(/\s*-\s*/);
items.push({
score: parseFloat(pair[0]),
name: pair[1]
});
})
// sort by score, descending
items.sort(function (a, b) { return b.score - a.score });
for (let i = 0; i < items.length; i++) {
console.log('cat' + (i+1) + '= ' + items[i].name);
console.log('cat' + (i+1) + 'score= '+ items[i].score);
}
Here is your updated snippet. You need to sort and then reverse. And change the order of some of your logic.
var product;
var finalString = `,,,0.003839795 - clothes ,0.171756425 - food ,0.00741361072561247 - electronics`
var finalArray = finalString.split(',');
finalArray.sort().reverse()
finalArray = finalArray.filter(function(it) {
return it.trim().length > 0
})
var rets=[];
for (var i = 0; i < finalArray.length; i++) {
var product = finalArray[i]
var pair = product.split(' - ');
var ret = {
product: pair[1],
score: pair[0]
}
rets.push(ret)
console.log(ret)
}
If I understand your question correctly, you begin with this input:
const arr = [
'0.003839795 - clothes',
'0.171756425 - food',
'0.00741361072561247 - electronics'
// ...
];
From there, you want to first sort the array in descending order (highest to lowest), then transform (map) the output into the below output order:
cat1 = food
cat1score = 0.171756425
cat2 = electronics
cat2score = 0.00741361072561247
cat3 = clothes
cat3score = 0.003839795
You can map => sort => reverse => format values using composable functions:
const arr = [
'0.003839795 - clothes',
'0.171756425 - food',
'0.00741361072561247 - electronics'
// ...
];
const toTuples = val => val.split(' - ');
const floatSort = (a, b) => parseFloat(a[0]) - parseFloat(b[0]);
const toOutputFormat = (tuple, index) => `
cat${index + 1} = ${tuple[1]}
cat${index + 1}score = ${tuple[0]}
`
arr.map(toTuples).sort(floatSort).reverse().map(toOutputFormat);
// initializing categories
categories = ["Food", "Electronics", "Cloths"];
// initializing scores
scores = [0.171, 0.0074, 0.0038];
// constructing array or array with categories and scores
out = [];
categories.forEach(function(d, i){
out.push([d, scores[i]]);
});
// sorting out array based on the score
out.sort(function(x, y) {
return y[1]-x[1];
});
Here is the code for you.
I have used to arrays then merged it into one ans then sorted the merged array on the basis of score.
Here's one thing that you can do:
consider this is your array:
let arr = ["0.003839795 - clothes" ,"0.171756425 - food","0.00741361072561247 - electronics"];
You can split each item of the array into two individual arrays per item.
So basically:
"0.003839795 - clothes" becomes ["0.003839795","clothes"]
This code snippet will do that:
let splittedArray = arr.map((value) => {
return value.split('-').map((v) => v.trim());
});
Now you can just sort them with this logic:
splittedArray.sort((x, y) => y[0] - x[0]);
Now, the array is sorted in ascending order and looks something like this:
[
[
"0.171756425",
"food"
],
[
"0.00741361072561247",
"electronics"
],
[
"0.003839795",
"clothes"
]
]
Now, you can just take out values out of this sorted array simply this way:
cat1 = splittedArray[0][1];
cat1score = splittedArray[0][0];
cat2 = splittedArray[1][1];
cat2score = splittedArray[1][0];
cat3= splittedArray[2][1];
cat3score = splittedArray[0][0];
You can also write these in a simple for loop and get this task done^

Sorting an Array of comma separated string using JavaScript

I came across with a weird requirement and I am struggling for last few hours to complete it. Below is my Array of string(just an example, the actual array contains around 2500 records):
var testArray = [
"130,839.9,855,837.3,848.65,3980489",
"129,875,875,828.1,833.25,6926078",
"138,891.3,893.3,865.2,868.75,5035618"
]
We have 3 element here of which each element is comma separated(each element have 6 item). i.e:
testArray[0] = "130,839.9,855,837.3,848.65,3980489"
My problem is, I wanted to sort testArray based on the first item of each element and convert it to array of array having all value into float, so the output would be:
[
[129, 875, 875, 828.1, 833.25, 6926078],
[130, 839.9, 855, 837.3, 848.65, 3980489],
[138, 891.3, 893.3, 865.2, 868.75, 5035618]
]
I am able to sort individual item but not the entire array as a whole, and I have tried using split and then sort with no luck.
Can someone help me out with this and please let me know if I am not clear.
Convert the array using Array#map within an Array#map, then use Array#sort on the converted array according to the [0] indices (a[0] - b[0]):
In ES5
var testArray = [
"130,839.9,855,837.3,848.65,3980489",
"129,875,875,828.1,833.25,6926078",
"138,891.3,893.3,865.2,868.75,5035618"
]
var converted = testArray.map(function (item) {
return item.split(',').map(function (num) {
return parseFloat(num);
});
})
console.log(converted)
var sorted = converted.sort(function (a, b) { return a[0] - b[0] })
console.log(sorted)
In ES6
const testArray = [
"130,839.9,855,837.3,848.65,3980489",
"129,875,875,828.1,833.25,6926078",
"138,891.3,893.3,865.2,868.75,5035618"
]
const converted = testArray.map(
item => item.split(',').map(
num => parseFloat(num)
)
)
console.log(converted)
const sorted = converted.sort((a, b) => a[0] - b[0])
console.log(sorted)
In ES6 (condensed)
const testArray = [
"130,839.9,855,837.3,848.65,3980489",
"129,875,875,828.1,833.25,6926078",
"138,891.3,893.3,865.2,868.75,5035618"
]
const convertedAndSorted = testArray
.map(n => n.split(',')
.map(num => parseFloat(num)))
.sort((a, b) => a[0] - b[0])
console.log(convertedAndSorted)
Just map the splitted and to number formatted values and sort by the first item.
var data = ["130,839.9,855,837.3,848.65,3980489", "129,875,875,828.1,833.25,6926078", "138,891.3,893.3,865.2,868.75,5035618"],
result = data
.map(s => s.split(',').map(Number))
.sort((a, b) => a[0] - b[0]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
var testArray = ["130,839.9,855,837.3,848.65,3980489","129,875,875,828.1,833.25,6926078","138,891.3,893.3,865.2,868.75,5035618"];
const output = [];
for (let i = 0; i < testArray.length; i++) {
var numbers = testArray[i].split(',');
for (let j = 0; j < numbers.length; j++) {
numbers[j] = +numbers[j];
}
output[i] = numbers;
}
output.sort(function(x, y) {
return x[0] - y[0];
});
or shorter
output = testArray.map(s => s.split(',')).map(e => e.map(n => +n)).sort((x, y) => x[0] - y[0]);
First convert each of the Strings to an array of floats values using Array.map() and parseFloat().
After that you can simply sort the array of arrays using Arrays.sort()
Try the following :
var arr = ["130,839.9,855,837.3,848.65,3980489","129,875,875,828.1,833.25,6926078","138,891.3,893.3,865.2,868.75,5035618"];
var result = arr.map((a)=> a.split(",").map((b)=>parseFloat(b))).sort((a,b)=> a[0] -b[0]);
console.log(result);

Transform JavaScript array

I'm getting array of data from server, but after come to jquery datatable i needs multidimention array .Is there any way to make it in jquery itself beflore pass it to datatables ?
My input format is :
["computer","program","laptop","monitor","mouse","keybord","cpu","harddrive"......]
Expected format :
[["computer","program","laptop","monitor"],["mouse","keybord","cpu","harddrive"],[....],[....]........]
Is there any method to parse data format ?
It doesn't take much more than a simple while loop to transform the array.
// This is the original data we get from the server
var input = ["computer","program","laptop","monitor","mouse","keybord","cpu","harddrive"];
// Make a copy of the input, so we don't destroy it
var data = input.slice(0);
// This is our output array
var output = [], group;
// A while loop will transform the plain array into a multidimensional array
while (data.length > 0) {
// Take the first four items
group = data.splice(0, 4);
// Make sure the group contains 4 items, otherwise pad with empty string
while (group.length < 4) {
group.push("");
}
// Push group into the output array
output.push(group);
}
// output = [["computer","program","laptop","monitor"],["mouse","keybord","cpu","harddrive"]]
Update: Beetroot-Beetroot's comment is no longer valid since we create a copy of the input.
I found this beautiful question sometime ago when i had a similar problem. This is a solution based (erm.. ripped out from there) on that :
var a = ["computer", "program", "laptop", "monitor", "mouse", "keybord", "cpu", "harddrive", "tablet"],
n = a.length / 4,
len = a.length,
out = [],
i = 0;
while (i < len) {
var size = Math.ceil((len - i) / n--);
out.push(a.slice(i, i + size));
i += size;
}
alert(JSON.stringify(out));
Message from the future ;) - now we have reduce:
function groupArray(array, groupSize) {
return array.reduce((a, b, i) => {
if (!i || !(i % groupSize)) a.push([])
a.slice(-1).pop().push(b)
return a
}, [])
}
console.log(groupArray(input, 4))
// [
// [ 'computer', 'program', 'laptop', 'monitor' ],
// [ 'mouse', 'keybord', 'cpu', 'harddrive' ]
// ]

Categories