Joining two collections efficiently? - javascript

I have run into an issue where I am trying to join two arrays similar to the ones below:
var participants = [
{id: 1, name: "abe"},
{id:2, name:"joe"}
];
var results = [
[
{question: 6, participantId: 1, answer:"test1"},
{question: 6, participantId: 2, answer:"test2"}
],
[
{question: 7, participantId: 1, answer:"test1"},
{question: 7, participantId: 2, answer:"test2"}
]
];
Using nested loops:
_.each(participants, function(participant) {
var row, rowIndex;
row = [];
var rowIndex = 2
return _.each(results, function(result) {
return _.each(result, function(subResult) {
var data;
data = _.find(subResult, function(part) {
return part.participantId === participant.id;
});
row[rowIndex] = data.answer;
return rowIndex++;
});
});
});
This works ok as long as the arrays are small, but once they get larger I am getting huge performance problems. Is there a faster way to combine two arrays in this way?
This is a slimmed down version of my real dataset/code. Please let me know if anything doesn't make sense.
FYI
My end goal is to create a collection of rows for each participant containing their answers. Something like:
[
["abe","test1","test1"],
["joe","test2","test2"]
]

The perf* is not from the for loops so you can change them to _ iteration if they gross you out
var o = Object.create(null);
for( var i = 0, len = participants.length; i < len; ++i ) {
o[participants[i].id] = [participants[i].name];
}
for( var i = 0, len = results.length; i < len; ++i ) {
var innerResult = results[i];
for( var j = 0, len2 = innerResult.length; j < len2; ++j) {
o[innerResult[j].participantId].push(innerResult[j].answer);
}
}
//The rows are in o but you can get an array of course if you want:
var result = [];
for( var key in o ) {
result.push(o[key]);
}
*Well if _ uses native .forEach then that's easily order of magnitude slower than for loop but still your problem is 4 nested loops right now so you might not even need the additional 10x after fixing that.

Here is a solution using ECMA5 methods
Javascript
var makeRows1 = (function () {
"use strict";
function reduceParticipants(previous, participant) {
previous[participant.id] = [participant.name];
return previous;
}
function reduceResult(previous, subResult) {
previous[subResult.participantId].push(subResult.answer);
return previous;
}
function filterParticipants(participant) {
return participant;
}
return function (participants, results) {
var row = participants.reduce(reduceParticipants, []);
results.forEach(function (result) {
result.reduce(reduceResult, row);
});
return row.filter(filterParticipants);
};
}());
This will not be as fast as using raw for loops, like #Esailija answer, but it's not as slow as you may think. It's certainly faster than using Underscore, like your example or the answer given by #Maroshii
Anyway, here is a jsFiddle of all three answers that demonstrates that they all give the same result. It uses quite a large data set, I don't know it compares to the size you are using. The data is generated with the following:
Javascript
function makeName() {
var text = "",
possible = "abcdefghijklmnopqrstuvwxy",
i;
for (i = 0; i < 5; i += 1) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
}
var count,
count2,
index,
index2,
participants = [],
results = [];
for (index = 0, count = 1000; index < count; index += 4) {
participants.push({
id: index,
name: makeName()
});
}
for (index = 0, count = 1000; index < count; index += 1) {
results[index] = [];
for (index2 = 0, count2 = participants.length; index2 < count2; index2 += 1) {
results[index].push({
question: index,
participantId: participants[index2].id,
answer: "test" + index
});
}
}
Finally, we have a jsperf that compares these three methods, run on the generated data set.

Haven't tested it with large amounts of data but here's an approach:
var groups = _.groupBy(_.flatten(results),'participantId');
var result =_.reduce(groups,function(memo,group) {
var user = _.find(participants,function(p) { return p.id === group[0].participantId; });
var arr = _.pluck(group,'answer');
arr.unshift(user.name);
memo.push(arr);
return memo ;
},[]);
The amounts of groups would be the amount of arrays that you'll have so then iterating over that with not grow exponentially as if you call _.each(_.each(_.each which can be quite expensive.
Again, should be tested.

Related

How to get all possible combination of jagged arrays? [duplicate]

This question already has answers here:
Cartesian product of multiple arrays in JavaScript
(35 answers)
Closed 1 year ago.
I'm having trouble coming up with code to generate combinations from n number of arrays with m number of elements in them, in JavaScript. I've seen similar questions about this for other languages, but the answers incorporate syntactic or library magic that I'm unsure how to translate.
Consider this data:
[[0,1], [0,1,2,3], [0,1,2]]
3 arrays, with a different number of elements in them. What I want to do is get all combinations by combining an item from each array.
For example:
0,0,0 // item 0 from array 0, item 0 from array 1, item 0 from array 2
0,0,1
0,0,2
0,1,0
0,1,1
0,1,2
0,2,0
0,2,1
0,2,2
And so on.
If the number of arrays were fixed, it would be easy to make a hard coded implementation. But the number of arrays may vary:
[[0,1], [0,1]]
[[0,1,3,4], [0,1], [0], [0,1]]
Any help would be much appreciated.
Here is a quite simple and short one using a recursive helper function:
function cartesian(...args) {
var r = [], max = args.length-1;
function helper(arr, i) {
for (var j=0, l=args[i].length; j<l; j++) {
var a = arr.slice(0); // clone arr
a.push(args[i][j]);
if (i==max)
r.push(a);
else
helper(a, i+1);
}
}
helper([], 0);
return r;
}
Usage:
cartesian([0,1], [0,1,2,3], [0,1,2]);
To make the function take an array of arrays, just change the signature to function cartesian(args) instead of using rest parameter syntax.
I suggest a simple recursive generator function:
// JS
function* cartesianIterator(head, ...tail) {
const remainder = tail.length ? cartesianIterator(...tail) : [[]];
for (let r of remainder) for (let h of head) yield [h, ...r];
}
// get values:
const cartesian = items => [...cartesianIterator(items)];
console.log(cartesian(input));
// TS
function* cartesianIterator<T>(items: T[][]): Generator<T[]> {
const remainder = items.length > 1 ? cartesianIterator(items.slice(1)) : [[]];
for (let r of remainder) for (let h of items.at(0)!) yield [h, ...r];
}
// get values:
const cartesian = <T>(items: T[][]) => [...cartesianIterator(items)];
console.log(cartesian(input));
You could take an iterative approach by building sub arrays.
var parts = [[0, 1], [0, 1, 2, 3], [0, 1, 2]],
result = parts.reduce((a, b) => a.reduce((r, v) => r.concat(b.map(w => [].concat(v, w))), []));
console.log(result.map(a => a.join(', ')));
.as-console-wrapper { max-height: 100% !important; top: 0; }
After doing a little research I discovered a previous related question:
Finding All Combinations of JavaScript array values
I've adapted some of the code from there so that it returns an array of arrays containing all of the permutations:
function(arraysToCombine) {
var divisors = [];
for (var i = arraysToCombine.length - 1; i >= 0; i--) {
divisors[i] = divisors[i + 1] ? divisors[i + 1] * arraysToCombine[i + 1].length : 1;
}
function getPermutation(n, arraysToCombine) {
var result = [],
curArray;
for (var i = 0; i < arraysToCombine.length; i++) {
curArray = arraysToCombine[i];
result.push(curArray[Math.floor(n / divisors[i]) % curArray.length]);
}
return result;
}
var numPerms = arraysToCombine[0].length;
for(var i = 1; i < arraysToCombine.length; i++) {
numPerms *= arraysToCombine[i].length;
}
var combinations = [];
for(var i = 0; i < numPerms; i++) {
combinations.push(getPermutation(i, arraysToCombine));
}
return combinations;
}
I've put a working copy at http://jsfiddle.net/7EakX/ that takes the array you gave earlier ([[0,1], [0,1,2,3], [0,1,2]]) and outputs the result to the browser console.
const charSet = [["A", "B"],["C", "D", "E"],["F", "G", "H", "I"]];
console.log(charSet.reduce((a,b)=>a.flatMap(x=>b.map(y=>x+y)),['']))
Just for fun, here's a more functional variant of the solution in my first answer:
function cartesian() {
var r = [], args = Array.from(arguments);
args.reduceRight(function(cont, factor, i) {
return function(arr) {
for (var j=0, l=factor.length; j<l; j++) {
var a = arr.slice(); // clone arr
a[i] = factor[j];
cont(a);
}
};
}, Array.prototype.push.bind(r))(new Array(args.length));
return r;
}
Alternative, for full speed we can dynamically compile our own loops:
function cartesian() {
return (cartesian.cache[arguments.length] || cartesian.compile(arguments.length)).apply(null, arguments);
}
cartesian.cache = [];
cartesian.compile = function compile(n) {
var args = [],
indent = "",
up = "",
down = "";
for (var i=0; i<n; i++) {
var arr = "$"+String.fromCharCode(97+i),
ind = String.fromCharCode(105+i);
args.push(arr);
up += indent+"for (var "+ind+"=0, l"+arr+"="+arr+".length; "+ind+"<l"+arr+"; "+ind+"++) {\n";
down = indent+"}\n"+down;
indent += " ";
up += indent+"arr["+i+"] = "+arr+"["+ind+"];\n";
}
var body = "var res=[],\n arr=[];\n"+up+indent+"res.push(arr.slice());\n"+down+"return res;";
return cartesian.cache[n] = new Function(args, body);
}
var f = function(arr){
if(typeof arr !== 'object'){
return false;
}
arr = arr.filter(function(elem){ return (elem !== null); }); // remove empty elements - make sure length is correct
var len = arr.length;
var nextPerm = function(){ // increase the counter(s)
var i = 0;
while(i < len)
{
arr[i].counter++;
if(arr[i].counter >= arr[i].length){
arr[i].counter = 0;
i++;
}else{
return false;
}
}
return true;
};
var getPerm = function(){ // get the current permutation
var perm_arr = [];
for(var i = 0; i < len; i++)
{
perm_arr.push(arr[i][arr[i].counter]);
}
return perm_arr;
};
var new_arr = [];
for(var i = 0; i < len; i++) // set up a counter property inside the arrays
{
arr[i].counter = 0;
}
while(true)
{
new_arr.push(getPerm()); // add current permutation to the new array
if(nextPerm() === true){ // get next permutation, if returns true, we got them all
break;
}
}
return new_arr;
};
Here's another way of doing it. I treat the indices of all of the arrays like a number whose digits are all different bases (like time and dates), using the length of the array as the radix.
So, using your first set of data, the first digit is base 2, the second is base 4, and the third is base 3. The counter starts 000, then goes 001, 002, then 010. The digits correspond to indices in the arrays, and since order is preserved, this is no problem.
I have a fiddle with it working here: http://jsfiddle.net/Rykus0/DS9Ea/1/
and here is the code:
// Arbitrary base x number class
var BaseX = function(initRadix){
this.radix = initRadix ? initRadix : 1;
this.value = 0;
this.increment = function(){
return( (this.value = (this.value + 1) % this.radix) === 0);
}
}
function combinations(input){
var output = [], // Array containing the resulting combinations
counters = [], // Array of counters corresponding to our input arrays
remainder = false, // Did adding one cause the previous digit to rollover?
temp; // Holds one combination to be pushed into the output array
// Initialize the counters
for( var i = input.length-1; i >= 0; i-- ){
counters.unshift(new BaseX(input[i].length));
}
// Get all possible combinations
// Loop through until the first counter rolls over
while( !remainder ){
temp = []; // Reset the temporary value collection array
remainder = true; // Always increment the last array counter
// Process each of the arrays
for( i = input.length-1; i >= 0; i-- ){
temp.unshift(input[i][counters[i].value]); // Add this array's value to the result
// If the counter to the right rolled over, increment this one.
if( remainder ){
remainder = counters[i].increment();
}
}
output.push(temp); // Collect the results.
}
return output;
}
// Input is an array of arrays
console.log(combinations([[0,1], [0,1,2,3], [0,1,2]]));
You can use a recursive function to get all combinations
const charSet = [["A", "B"],["C", "D", "E"],["F", "G", "H", "I"]];
let loopOver = (arr, str = '', final = []) => {
if (arr.length > 1) {
arr[0].forEach(v => loopOver(arr.slice(1), str + v, final))
} else {
arr[0].forEach(v => final.push(str + v))
}
return final
}
console.log(loopOver(charSet))
This code can still be shorten using ternary but i prefer the first version for readability 😊
const charSet = [["A", "B"],["C", "D", "E"],["F", "G", "H", "I"]];
let loopOver = (arr, str = '') => arr[0].map(v => arr.length > 1 ? loopOver(arr.slice(1), str + v) : str + v).flat()
console.log(loopOver(charSet))
Another implementation with ES6 recursive style
Array.prototype.cartesian = function(a,...as){
return a ? this.reduce((p,c) => (p.push(...a.cartesian(...as).map(e => as.length ? [c,...e] : [c,e])),p),[])
: this;
};
console.log(JSON.stringify([0,1].cartesian([0,1,2,3], [[0],[1],[2]])));

Find all sentence permutations with synonymous words? [duplicate]

This question already has answers here:
Cartesian product of multiple arrays in JavaScript
(35 answers)
Closed 1 year ago.
I'm having trouble coming up with code to generate combinations from n number of arrays with m number of elements in them, in JavaScript. I've seen similar questions about this for other languages, but the answers incorporate syntactic or library magic that I'm unsure how to translate.
Consider this data:
[[0,1], [0,1,2,3], [0,1,2]]
3 arrays, with a different number of elements in them. What I want to do is get all combinations by combining an item from each array.
For example:
0,0,0 // item 0 from array 0, item 0 from array 1, item 0 from array 2
0,0,1
0,0,2
0,1,0
0,1,1
0,1,2
0,2,0
0,2,1
0,2,2
And so on.
If the number of arrays were fixed, it would be easy to make a hard coded implementation. But the number of arrays may vary:
[[0,1], [0,1]]
[[0,1,3,4], [0,1], [0], [0,1]]
Any help would be much appreciated.
Here is a quite simple and short one using a recursive helper function:
function cartesian(...args) {
var r = [], max = args.length-1;
function helper(arr, i) {
for (var j=0, l=args[i].length; j<l; j++) {
var a = arr.slice(0); // clone arr
a.push(args[i][j]);
if (i==max)
r.push(a);
else
helper(a, i+1);
}
}
helper([], 0);
return r;
}
Usage:
cartesian([0,1], [0,1,2,3], [0,1,2]);
To make the function take an array of arrays, just change the signature to function cartesian(args) instead of using rest parameter syntax.
I suggest a simple recursive generator function:
// JS
function* cartesianIterator(head, ...tail) {
const remainder = tail.length ? cartesianIterator(...tail) : [[]];
for (let r of remainder) for (let h of head) yield [h, ...r];
}
// get values:
const cartesian = items => [...cartesianIterator(items)];
console.log(cartesian(input));
// TS
function* cartesianIterator<T>(items: T[][]): Generator<T[]> {
const remainder = items.length > 1 ? cartesianIterator(items.slice(1)) : [[]];
for (let r of remainder) for (let h of items.at(0)!) yield [h, ...r];
}
// get values:
const cartesian = <T>(items: T[][]) => [...cartesianIterator(items)];
console.log(cartesian(input));
You could take an iterative approach by building sub arrays.
var parts = [[0, 1], [0, 1, 2, 3], [0, 1, 2]],
result = parts.reduce((a, b) => a.reduce((r, v) => r.concat(b.map(w => [].concat(v, w))), []));
console.log(result.map(a => a.join(', ')));
.as-console-wrapper { max-height: 100% !important; top: 0; }
After doing a little research I discovered a previous related question:
Finding All Combinations of JavaScript array values
I've adapted some of the code from there so that it returns an array of arrays containing all of the permutations:
function(arraysToCombine) {
var divisors = [];
for (var i = arraysToCombine.length - 1; i >= 0; i--) {
divisors[i] = divisors[i + 1] ? divisors[i + 1] * arraysToCombine[i + 1].length : 1;
}
function getPermutation(n, arraysToCombine) {
var result = [],
curArray;
for (var i = 0; i < arraysToCombine.length; i++) {
curArray = arraysToCombine[i];
result.push(curArray[Math.floor(n / divisors[i]) % curArray.length]);
}
return result;
}
var numPerms = arraysToCombine[0].length;
for(var i = 1; i < arraysToCombine.length; i++) {
numPerms *= arraysToCombine[i].length;
}
var combinations = [];
for(var i = 0; i < numPerms; i++) {
combinations.push(getPermutation(i, arraysToCombine));
}
return combinations;
}
I've put a working copy at http://jsfiddle.net/7EakX/ that takes the array you gave earlier ([[0,1], [0,1,2,3], [0,1,2]]) and outputs the result to the browser console.
const charSet = [["A", "B"],["C", "D", "E"],["F", "G", "H", "I"]];
console.log(charSet.reduce((a,b)=>a.flatMap(x=>b.map(y=>x+y)),['']))
Just for fun, here's a more functional variant of the solution in my first answer:
function cartesian() {
var r = [], args = Array.from(arguments);
args.reduceRight(function(cont, factor, i) {
return function(arr) {
for (var j=0, l=factor.length; j<l; j++) {
var a = arr.slice(); // clone arr
a[i] = factor[j];
cont(a);
}
};
}, Array.prototype.push.bind(r))(new Array(args.length));
return r;
}
Alternative, for full speed we can dynamically compile our own loops:
function cartesian() {
return (cartesian.cache[arguments.length] || cartesian.compile(arguments.length)).apply(null, arguments);
}
cartesian.cache = [];
cartesian.compile = function compile(n) {
var args = [],
indent = "",
up = "",
down = "";
for (var i=0; i<n; i++) {
var arr = "$"+String.fromCharCode(97+i),
ind = String.fromCharCode(105+i);
args.push(arr);
up += indent+"for (var "+ind+"=0, l"+arr+"="+arr+".length; "+ind+"<l"+arr+"; "+ind+"++) {\n";
down = indent+"}\n"+down;
indent += " ";
up += indent+"arr["+i+"] = "+arr+"["+ind+"];\n";
}
var body = "var res=[],\n arr=[];\n"+up+indent+"res.push(arr.slice());\n"+down+"return res;";
return cartesian.cache[n] = new Function(args, body);
}
var f = function(arr){
if(typeof arr !== 'object'){
return false;
}
arr = arr.filter(function(elem){ return (elem !== null); }); // remove empty elements - make sure length is correct
var len = arr.length;
var nextPerm = function(){ // increase the counter(s)
var i = 0;
while(i < len)
{
arr[i].counter++;
if(arr[i].counter >= arr[i].length){
arr[i].counter = 0;
i++;
}else{
return false;
}
}
return true;
};
var getPerm = function(){ // get the current permutation
var perm_arr = [];
for(var i = 0; i < len; i++)
{
perm_arr.push(arr[i][arr[i].counter]);
}
return perm_arr;
};
var new_arr = [];
for(var i = 0; i < len; i++) // set up a counter property inside the arrays
{
arr[i].counter = 0;
}
while(true)
{
new_arr.push(getPerm()); // add current permutation to the new array
if(nextPerm() === true){ // get next permutation, if returns true, we got them all
break;
}
}
return new_arr;
};
Here's another way of doing it. I treat the indices of all of the arrays like a number whose digits are all different bases (like time and dates), using the length of the array as the radix.
So, using your first set of data, the first digit is base 2, the second is base 4, and the third is base 3. The counter starts 000, then goes 001, 002, then 010. The digits correspond to indices in the arrays, and since order is preserved, this is no problem.
I have a fiddle with it working here: http://jsfiddle.net/Rykus0/DS9Ea/1/
and here is the code:
// Arbitrary base x number class
var BaseX = function(initRadix){
this.radix = initRadix ? initRadix : 1;
this.value = 0;
this.increment = function(){
return( (this.value = (this.value + 1) % this.radix) === 0);
}
}
function combinations(input){
var output = [], // Array containing the resulting combinations
counters = [], // Array of counters corresponding to our input arrays
remainder = false, // Did adding one cause the previous digit to rollover?
temp; // Holds one combination to be pushed into the output array
// Initialize the counters
for( var i = input.length-1; i >= 0; i-- ){
counters.unshift(new BaseX(input[i].length));
}
// Get all possible combinations
// Loop through until the first counter rolls over
while( !remainder ){
temp = []; // Reset the temporary value collection array
remainder = true; // Always increment the last array counter
// Process each of the arrays
for( i = input.length-1; i >= 0; i-- ){
temp.unshift(input[i][counters[i].value]); // Add this array's value to the result
// If the counter to the right rolled over, increment this one.
if( remainder ){
remainder = counters[i].increment();
}
}
output.push(temp); // Collect the results.
}
return output;
}
// Input is an array of arrays
console.log(combinations([[0,1], [0,1,2,3], [0,1,2]]));
You can use a recursive function to get all combinations
const charSet = [["A", "B"],["C", "D", "E"],["F", "G", "H", "I"]];
let loopOver = (arr, str = '', final = []) => {
if (arr.length > 1) {
arr[0].forEach(v => loopOver(arr.slice(1), str + v, final))
} else {
arr[0].forEach(v => final.push(str + v))
}
return final
}
console.log(loopOver(charSet))
This code can still be shorten using ternary but i prefer the first version for readability 😊
const charSet = [["A", "B"],["C", "D", "E"],["F", "G", "H", "I"]];
let loopOver = (arr, str = '') => arr[0].map(v => arr.length > 1 ? loopOver(arr.slice(1), str + v) : str + v).flat()
console.log(loopOver(charSet))
Another implementation with ES6 recursive style
Array.prototype.cartesian = function(a,...as){
return a ? this.reduce((p,c) => (p.push(...a.cartesian(...as).map(e => as.length ? [c,...e] : [c,e])),p),[])
: this;
};
console.log(JSON.stringify([0,1].cartesian([0,1,2,3], [[0],[1],[2]])));

array.push only the last variable in a for loop javascript

i'm actually asking myself why the following code is not working properly i found the solution but it's a bit tricky and i don't like this solution
Here is the code and the problem:
function powerSet( list ){
var set = [],
listSize = list.length,
combinationsCount = (1 << listSize),
combination;
for (var i = 1; i < combinationsCount ; i++ ){
var combination = [];
for (var j=0;j<listSize;j++){
if ((i & (1 << j))){
combination.push(list[j]);
}
}
set.push(combination);
}
return set;
}
function getDataChartSpe(map) {
var res = {};
for (var i in map) {
console.log("\n\n");
var dataSpe = {certif: false,
experience: 0,
expert: false,
grade: 1,
last: 100,
name: undefined
};
var compMatchList = [];
for (var j in map[i].comps_match) {
var tmp = map[i].comps_match[j];
compMatchList.push(tmp.name)
}
var tmpList = powerSet(compMatchList);
var lol = [];
lol.push(map[i].comps_match);
for (elem in tmpList) {
console.log("mdr elem === " + elem + " tmplist === " + tmpList);
var tmp = tmpList[elem];
dataSpe.name = tmpList[elem].join(" ");
lol[0].push(dataSpe);
}
console.log(lol);
}
return res;
}
now here is the still the same code but working well :
function powerSet( list ){
var set = [],
listSize = list.length,
combinationsCount = (1 << listSize),
combination;
for (var i = 1; i < combinationsCount ; i++ ){
var combination = [];
for (var j=0;j<listSize;j++){
if ((i & (1 << j))){
combination.push(list[j]);
}
}
set.push(combination);
}
return set;
}
function getDataChartSpe(map) {
var res = {};
var mapBis = JSON.parse(JSON.stringify(map));
for (var i in map) {
var compMatchList = [];
for (var j in map[i].comps_match) {
var tmp = map[i].comps_match[j];
compMatchList.push(tmp.name)
}
var tmpList = powerSet(compMatchList);
mapBis[i].comps_match = [];
for (elem in tmpList) {
tmpList[elem].sort();
mapBis[i].comps_match.push({certif: false,
experience: 0,
expert: false,
grade: 1,
last: 100,
name: tmpList[elem].join(", ")});
}
}
return mapBis;
}
Actually it's a bit disapointig for me because it's exactly the same but the 1st one doesn't work and the second one is working.
so if anyone can help me to understand what i'm doing wrong it'll be with pleasure
ps: i'm sorry if my english is a bit broken
In the first version, you build one dataSpe object and re-use it over and over again. Each time this runs:
lol[0].push(dataSpe);
you're pushing a reference to the same single object onto the array.
The second version of the function works because it builds a new object each time:
mapBis[i].comps_match.push({certif: false,
experience: 0,
expert: false,
grade: 1,
last: 100,
name: tmpList[elem].join(", ")});
That object literal passed to .push() will create a new, distinct object each time that code runs.

add elements of an array javascript

Ok, this might be easy for some genius out there but I'm struggling...
This is for a project I'm working on with a slider, I want an array the slider can use for snap points/increments... I'm probably going about this in a mental way but its all good practice! Please help.
var frootVals = [1,2,3,4,5];
var frootInc = [];
for (i=0; i<=frootVals.length; i++) {
if (i == 0){
frootInc.push(frootVals[i]);
}
else{
frootInc.push(frootInc[i-1] += frootVals[i])
}
};
What I'm trying to do is create the new array so that its values are totals of the array elements in frootVals.
The result I'm looking for would be this:
fruitInc = [1,3,6,10,15]
For a different take, I like the functional approach:
var frootVals = [1,2,3,4,5];
var frootInc = [];
var acc = 0;
frootVals.forEach(function(i) {
acc = acc + i;
frootInc.push(acc);
});
var frootVals = [1,2,3,4,5]
, frootInc = [];
// while i < length, <= will give us NaN for last iteration
for ( i = 0; i < frootVals.length; i++) {
if (i == 0) {
frootInc.push(frootVals[i]);
} else {
// rather than frootIne[ i-1 ] += ,
// we will just add both integers and push the value
frootInc.push( frootInc[ i-1 ] + frootVals[ i ] )
}
};
There were a few things wrong with your code check out the commenting in my code example. Hope it helps,
This will do:
var frootVals = [1,2,3,4,5];
var frootInc = [];
for (i=0; i < frootVals.length; i++) { // inferior to the length of the array to avoid iterating 6 times
if (i == 0) {
frootInc.push(frootVals[i]);
}
else {
frootInc.push(frootInc[i-1] + frootVals[i]) // we add the value, we don't reassign values
}
};
alert(JSON.stringify(frootInc));
jsfiddle here: http://jsfiddle.net/f01yceo4/
change your code to:
var frootVals = [1,2,3,4,5];
var frootInc = [frootvals[0]]; //array with first item of 'frootVals' array
for (i=1; i<frootVals.length; i++) {
frootInc.push(frootInc[i-1] + frootVals[i]); //remove '='
}
Here's a very simple pure functional approach (no vars, side-effects, or closures needed):
[1,2,3,4,5].map(function(a){return this[0]+=a;}, [0]);
// == [1, 3, 6, 10, 15]
if you name and un-sandwich the function, you can use it over and over again, unlike a hard-coded var name, property name, or for-loop...

What's an elegant way to define an array "grouping" utility function?

Suppose this is my input: "0.1412898495873448 -0.03307049805768848 -0.0002348551519150674 0.0007142371877833145 -0.01250041632738383 0.4052674201000387 -0.02541421100956797 -0.02842612870208528 1803.701163338969 1796.443677744862 1986.31441429052 1442.354524622483"
Sylvester has this constructor. It requires a nested array.
It's simple enough to make the string into an array of numbers using String.split() and map and friends.
Something not unlike this:
var nums = line.split(/ |\n/);
nums.map(function(num) {return parseFloat(num);});
But I think Javascript is missing a functional-style function that can "group" my 16-array into 4 rows of 4 numbers.
I thought maybe a JS utility library such as underscore might have me covered.
Doesn't look like it.
What's the most elegant and/or efficient way to do this? Do I have to use a for loop?
function nest(array, range) {
var l = array.length;
var ret = [];
var cur = null;
for (var i=0; i<l; ++i) {
if (!(i%range)) {
cur = [];
ret.push(cur);
}
cur.push(array[i]);
}
return ret;
}
Node tells me this works:
> function nest(array, range) {
... var l = array.length;
... var ret = [];
... var cur = null;
... for (var i=0; i<l; ++i) {
..... if (!(i%range)) {
....... cur = [];
....... ret.push(cur);
....... }
..... cur.push(array[i]);
..... }
... return ret;
... }
undefined
> nest([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],4)
[ [ 0, 1, 2, 3 ],
[ 4, 5, 6, 7 ],
[ 8, 9, 10, 11 ],
[ 12, 13, 14, 15 ] ]
>
Can anyone think of a better way to do it?
There are several ways to split an array into sub-arrays
var line = "0.1412898495873448 -0.03307049805768848 -0.0002348551519150674 0.0007142371877833145 -0.01250041632738383 0.4052674201000387 -0.02541421100956797 -0.02842612870208528 1803.701163338969 1796.443677744862 1986.31441429052 1442.354524622483";
var nums = line.split(/ |\n/).map(parseFloat);
var columns = 4;
var nested = [];
for (var i=0; i < nums.length; i+=columns) {
nested.push(nums.slice(i, i+columns));
}
console.log(nested);
Or functional way:
var nested = Array.apply(null, new Array((nums.length/columns)|0))
.map(function(item, index) {
return nums.slice(index*columns, (index+1) * columns);
});
Using splice instead of slice seems to be much better, though it might be due to badly written tests
var columns = 4;
var nested = [];
for (var i=nums.length/columns; i--;) { // gives expected result if columns===0
nested.push(nums.splice(0,columns));
}
Alternatively:
var columns = 4;
var nested = [];
while(nums.length) nested.push(nums.splice(0,columns));
Here is my approach,
var nums = line.split(/ |\n/);
var temp=[],output=[],i=0;
while(i<nums.length){
temp.push(i);
if(i%4 == 0){
output.push(temp);
temp=[];
}
i++
}
console.dir(output);
For my senses this is prettty fast!
check this performance test against the accepted answer
http://jsperf.com/comparing-use-of-modulo-vs-slice-in-a-special-case

Categories

Resources