Javascript Nested Loop Pushing to Array - javascript

I am relatively new to programming and am having some issues with a project I am working on.
msg.newCG2 = [];
for(i=0;i<msg.newCG.length;i++){
for(j=0;j<msg.campaignGroup.length;i++){
if(msg.campaignGroup[j].col10 === msg.newCG[j]){
msg.groupTotals = msg.groupTotals + msg.campaignGroup[j].col11;
}
msg.newCG2.push(msg.newCG[i], msg.groupTotals)
}
}
Basically, for each one of the "IDs" (integers) in msg.newCG, I want to look for each ID in msg.campaignGroup and sum up the totals for all listings with the same ID, from msg.campaignGroup.col11 - then push the ID and the totals to a new array - msg.newCG2.
When I run the code, the first item sent through processes, but grinds to a halt because of memory. I assume this is because of an error in my code.
Where did this code go wrong? I am sure that there are better ways to do this as a whole, but I am curious where I went wrong.

There is a typo in your second for loop and the push needs to happen inside the outer loop.
msg.newCG2 = [];
for(i=0;i<msg.newCG.length;i++){
for(j=0;j<msg.campaignGroup.length;j++){
if(msg.campaignGroup[j].col10 === msg.newCG[i]){
msg.groupTotals = msg.groupTotals + msg.campaignGroup[j].col11;
}
}
msg.newCG2.push(msg.newCG[i], msg.groupTotals)
}

How about:
msg.newCG2 = [];
for (i=0; i < msg.newCG.length; i++) {
var groupTotal = 0;
for (j=0; j < msg.campaignGroup.length; j++) {
if (msg.campaignGroup[j].col10 === msg.newCG[i]){
groupTotal = groupTotal + msg.campaignGroup[j].col11
}
}
msg.newCG2.push(groupTotal)
}

Rather than looping 1.2M times, it would be more efficient to use a single-pass over the 4000 campaign groups, grouping by id to create an array of totals for all ids -- I like using the reduce() function for this:
var cgMap = msg.campaignGroups.reduce(function(arr, grp) {
var grpid = grp.col10;
var count = grp.col11;
var total = arr[grpid] || 0;
arr[grpid] = total + count;
},
[]);
I know, the reduce(...) function is not the easiest to grok, but it takes the second arg (the empty array) and passes it, along with each campaign group object in turn, to that inline function. The result should be a simple array of group totals (from col11), indexed by the group id (from col10).
Now, it's just a matter of returning the totals for those 300 ids found in msg.newCG -- and this map() function does that for us:
var cgOut = msg.newCG.map(function(gid) {
return cgMap[gid]; // lookup the total by group id
}
);
I've made some assumptions here, like the group ids are not terribly large integers, and are rather closely spaced (not too sparse). From the original code, I was not able to determine the format of the data you are wanting to return in msg.newCG2. The final push() function would append 2 integers onto the array -- the output group id and the total for that group. Having pairs of group ids and totals interleaved in a flat array is not a very useful data structure. Perhaps you meant to place the total value into an array, indexed by the group id? If so, you could re-write that line as:
msg.newCG2[msg.newCG[i]] = msg.groupTotals;

Related

How to edit the highest 5 values of an array in javascript?

I have a single 1d array storing a series of scores. My end goal is to have the 5 highest scores with brackets around them ( e.g. (score) ) for me to then format and output onto the display. In the case where there are duplicate scores, the first occurrences would be bracketed, up to that 5 top values.
So for example:
[9,8,10,9,6,8,6,5,4,4,3,3,6] would become [(9),(8),(10),(9),6,(8),6,5,4,4,8,3,8]
What I've tried so far is this:
var topvals = scores.sort((a,b) => b-a).slice(0,5);
for(var j=0; j< scores.length; j++){
if(topvals.length==0){
break;
}else if(topvals.includes(scores[j])){
scores[j] = "(" + scores[j] + ")";
topvals.splice(topvals.indexOf(scores[j]),1);
}
}
With the idea that topvals is an array containing the top 5 values, and I then loop through scores looking for those values, removing them each time.
What this results in is the first 5 values of scores having brackets around them.
I'm happy to go a completely different route with this, or just fix what I've done so far. Thanks in advance.
sort with indexes attached. Use index positions to change to desired format. O(N log N) for the sort.
scores = [9,8,10,9,6,8,6,5,4,4,3,3,6]
scores.map((n,i)=>({n,i})) // each object as n: number, i: index
.sort((a,b)=>a.n-b.n).slice(-5) // sort, slice top 5
.forEach(({i})=>scores[i]=`(${scores[i]})`) // add parens by indexes
console.log(scores)
If you have very, very large data sets and need something closer to O(N), you'll want to implement a pivot selecting algorithm. Just sorting is simpler.
The call to sort() sorts scores in place, which means it changes the scores array. So that is why you need to clone it first, then sort, then slice. Also you probably want to eliminate duplicates from your scores. I linked a stack overflow answer that describes how to do that. Since filter does not filter scores in place, but rather returns a new array, you don't need to explicitly call slice(0) to clone scores.
var scores = [9,8,10,9,6,8,6,5,4,4,3,3,6];
// Function from linked SO answer.
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
// Filter unique scores into copy of array, then slice the top 5.
var topvals = scores.filter(onlyUnique).sort((a,b) => b-a).slice(0,5);
for (var j=0; j< scores.length; j++) {
if( topvals.length==0) {
break;
} else if(topvals.includes(scores[j])) {
scores[j] = "(" + scores[j] + ")";
topvals.splice(topvals.indexOf(scores[j]),1);
}
}
Get all unique values in a JavaScript array (remove duplicates)

Picking random values from array and creating new array

I have an array, and need to pick three random values from that array. These will the be put in a new array and I will be able to see the new array on my website. I also have to make sure that no value gets picked twice.
This is what I have so far:
var student = ["Hans","Ole","Nils","Olav","Per","Knut","Line","Pia"];
var velg = student[Math.floor(Math.random() * student.length)];
I'm thinking I should add an id to my HTML, so the new array will show on my website, but I'm not sure about the rest.
First sort it randomly and then get first three:
student
.sort(function(){
return Math.random() - 0.5;
})
.slice(0,3)
Since Math.random() returns random value between 0 and 1, while sort expects values to be positive or negative to determine order we, we need to subtract 0.5 to make those negatives possible.
You could try something like this in a loop
var students = ["Hans","Ole","Nils","Olav","Per","Knut","Line","Pia"];
var randomStudents = [];
for(var i = 0; i < 3; i++) {
var velg = student[Math.floor(Math.random() * students.length)];
randomStudents.push(velg);
}
Note that this can add duplicate students to the array. You should check if student is already in the array and try again.
Keyword for that would be recursion.
https://www.codecademy.com/courses/javascript-lesson-205/0/1

Javascript Loop within a loop Logic to produce Specific output

I good with php but javascript is not good with me, my english also not very good but im explaining everything as best and slowly in step by step.
I make calculation for charges for a telecom service using javascript. I know similar logic with in php has to be used but Then I stuck.
/prices is json from server as see below.
var prices = [{"brand":"Airtel (Waridtel) Ug","numbering":"25670","price":"40"},
{"brand":"UTL ug","numbering":"25671","price":"30"},
{"brand":"Airtel Ug","numbering":"25675","price":"40"},
{"brand":"MTN Ug","numbering":"25677","price":"20"},
{"brand":"MTN Ug","numbering":"25678","price":"20"},
{"brand":"Orange Ug","numbering":"25679","price":"30"},
{"brand":"Safaricom Ke","numbering":"25470","price":"30"},
{"brand":"Safaricom Ke","numbering":"25471","price":"30"}]
a user provide the various telephone Numbers in comma seperation string
//contacts
cdest = '245772306640,256754306640,25471624525,2567825689754,2563989654285,4588623566', 256782658974;
we use brand prefix to find brand.
if contacts prefix is equivalent to price numbering in json, then that is conctact which is owned by that brand. I calculate how many contact per brand and total cost basing on contact list user provide.
Expecting output is shown below.
brand(number of contacts)::total cost
Example MTN Ug(3)::60 , Airtel Ug(1), Safaricom Ke(1)::30,
Im also like to place any number with no a brand under a universal brand wich is called International with no a price assign as see below example
International(2)::default,
Here Im very confuse with two loops, im not understand how I can loop , count and echo but Im good with transfer of php knowledge so far I create double loop, but then I confuse again.
//explode numbers;
dest = cdest.split(",");
// loop through brands and numbers.
//plan is to create create array like (brand1 =>countbrand1,brand2=>countbrand3, etc)
cd = Array();
for( var i=0; i<dest.length; i++ )
{
$x.each(prices, function(index, m) {
if(dest[i].indexOf(m.numbering)==0)
{
cd[m.brand]+=1;
} else
cd[International]+=1;
});
}
In php I simply loop on cd now and get output. I try for js, but dont know how to echo the key of array in Javascript. javascript is bullying me whole day. If you make good answer, please you show in slow step by step. Im very thankful.
You can check this fiddle that I have created from your code. May help you
http://jsfiddle.net/GSeZ9/
var prices = [{"brand":"Airtel (Waridtel) Ug","numbering":"25670","price":"40"},
{"brand":"UTL ug","numbering":"25671","price":"30"},
{"brand":"Airtel Ug","numbering":"25675","price":"40"},
{"brand":"MTN Ug","numbering":"25677","price":"20"},
{"brand":"MTN Ug","numbering":"25678","price":"20"},
{"brand":"Orange Ug","numbering":"25679","price":"30"},
{"brand":"Safaricom Ke","numbering":"25470","price":"30"},
{"brand":"Safaricom Ke","numbering":"25471","price":"30"}];
var cdest = '245772306640,256754306640,25471624525,2567825689754,2563989654285,4588623566, 256782658974';
var dest = cdest.split(",");
// loop through brands and numbers.
//plan is to create create array like (brand1 =>countbrand1,brand2=>countbrand3, etc)
var cd = {};
for( var i=0; i<dest.length; i++ )
{
$.each(prices, function(index, m) {
// if(dest[i].toString().indexOf(m.numbering.toString()) != -1 )
//alert(dest[i].indexOf(m.numbering));
if(dest[i].toString().indexOf(m.numbering.toString()) == 0)
{
if( cd[m.brand] == undefined)
cd[m.brand] = 1;
else
cd[m.brand]+=1;
} //else
//cd[International]+=1;
});
}
for (var key in cd) {
alert(key + " = " + cd[key]);
}
In php arrays are used for many things, but in javascript arrays don't have keys. Objects do. What you have in the prices variable is an array of objects. To loop an object you can use the following syntax:
for (var key in p) {
// do something
// the key has the name `key` and to get the value, you
// can type p[key]
}
This is your inner loop. You got the outer loop right. This is how you will loop an array with the name dest:
for( var i=0; i<dest.length; i++ ) {
var member = dest[i];
// do something with member
}
With the outer loop we will get every object in the array and assign it to a variable with the name member. (You may think of a better name, of course)
So your final code will have to look something like this:
for( var i=0; i<dest.length; i++ ) {
var memeber = dest[i];
for (var key in member) {
// do something with the keys and values - print them for example.
}
}

Reordering an array in javascript

there are many questions/answers dealing with this topic. None match my specific case. Hopefully someone can help:
I have an array of indexes such as:
var indexes = [24, 48, 32, 7, 11];
And an array of objects that look similar to this:
var items = [{
name : "whatever",
selected : false,
loading : true,
progress : 55,
complete : false
},
{
name : "whatever 2",
selected : false,
loading : false,
progress : 100,
complete : true
}];
Each integer within the indexes array corresponds to the actual index of an object within the items array.
Lastly I have a variable which defines the new insert position within the items array:
var insertindex = ??
What I would like to do is to take all objects in the items array that have the indexes stored in the indexes array, remove them, then finally place them back, all next to each other at a specified index defined by the variable insertindex.
I have been trying to use splice() by copying the objects at each index to a temporary array, then removing them from the original array, then finally looping through this new temporary array and putting them back into the original items array at the new positions, but seems to be hitting a mental brick wall and cannot get it to work correctly.
To summarize, I simply want to take all objects from the items array that match an index defined in the indexes array, put them together and reinsert them at a predefined index, back into the items array.
To help with conceptual visualization. If you think of the app as a javascript file manager, allowing the reordering of multiple file selections which do not have to be adjacent. The indexes array defining the current selection and the items array defining the list of files. And finally the rearoderindex defines the new insert position that all selected files should move to.
EDIT: As was rightly suggested here is the code I am playing with right now:
function reorder(items, indexes, insertindex){
var offset = 0;
var itemscopy = items.slice(0); //make shallow copy of original array
var temparray = new Array(); // create temporary array to hold pulled out objects
//loop through selected indexes and copy each into temp array
for(var i=0, len=indexes.length; i<len; i++){
array[i] = itemscopy[self.cache.selecteditems[i]];
}
//remove all selected items from items array
for(var i=0, len=indexes.length; i<len; i++){
items.splice(indexes[i], 1);
}
//finally loop through new temp array and insert the items back into the items array at the specified index, increasing the index each iteration using the offset variable.
for(var i=0, len=temparray.length; i<len; i++){
items.splice((insertindex+offset), 0, array[i]);
offset++;
}
}
I'm aware this is pretty horrible and that looping three times should not be necessary. But I've been trying lots of different methods, some working when reordering in one direction, some in the other an mostly, not at all. I figured I would look to optimize the function later, once I have it working with accuracy.
I'm certain I must be doing something extremely stupid or completely overlooking something, but for the life of me I can't work out what right now.
If you don't care about order of indexes array, I'd suggest another short solution:
items.splice.apply(items, [insertIndex, 0].concat(indexes.sort(function(a, b) {
return a - b;
})).map(function(i, p) {
return p > 1 ? items.splice(i - p + 2, 1).pop() : i;
}));
DEMO: http://jsfiddle.net/T83fB/
To make it short I used Array.map() method, which is not supported by old IE browsers. However it is always easy to use a shim from MDN.
You can use the .splice() function to add elements to an array, as well as removing items from it. The general principle is:
Sort indexes into ascending numeric order
Iterate over indexes, removing the element at that index (adjusting for the number of removed items) and storing it in a removedItems array
Add the removedItems array back in at the required index
The code to do that would look something like this:
var removedItems = [];
// sort indexes
indexes.sort(function(a, b) {
if(a < b) return -1;
else if(b < a) return 1;
return 0;
});
for(var i = 0; i < indexes.length; i++) {
var index = indexes[i];
removedItems.push(items.splice(index - removedItems.length, 1));
}
var insertIndex = 1;
items.splice.apply(items, [insertIndex, 0].concat(removedItems));
Take a look at this jsFiddle demo.

Can I select 2nd element of a 2 dimensional array by value of the first element in Javascript?

I have a JSON response like this:
var errorLog = "[[\"comp\",\"Please add company name!\"],
[\"zip\",\"Please add zip code!\"],
...
Which I'm deserializing like this:
var log = jQuery.parseJSON(errorLog);
Now I can access elements like this:
log[1][1] > "Please add company name"
Question:
If I have the first value comp, is there a way to directly get the 2nd value by doing:
log[comp][1]
without looping through the whole array.
Thanks for help!
No. Unless the 'value' of the first array (maybe I should say, the first dimension, or the first row), is also it's key. That is, unless it is something like this:
log = {
'comp': 'Please add a company name'
.
.
.
}
Now, log['comp'] or log.comp is legal.
There are two was to do this, but neither avoids a loop. The first is to loop through the array each time you access the items:
var val = '';
for (var i = 0; i < errorLog.length; i++) {
if (errorLog[i][0] === "comp") {
val = errorLog[i][1];
break;
}
}
The other would be to work your array into an object and access it with object notation.
var errors = {};
for (var i = 0; i < errorLog.length; i++) {
errors[errorLog[i][0]] = errorLog[i][1];
}
You could then access the relevant value with errors.comp.
If you're only looking once, the first option is probably better. If you may look more than once, it's probably best to use the second system since (a) you only need to do the loop once, which is more efficient, (b) you don't repeat yourself with the looping code, (c) it's immediately obvious what you're trying to do.
No matter what you are going to loop through the array somehow even it is obscured for you a bit by tools like jQuery.
You could create an object from the array as has been suggested like this:
var objLookup = function(arr, search) {
var o = {}, i, l, first, second;
for (i=0, l=arr.length; i<l; i++) {
first = arr[i][0]; // These variables are for convenience and readability.
second = arr[i][1]; // The function could be rewritten without them.
o[first] = second;
}
return o[search];
}
But the faster solution would be to just loop through the array and return the value as soon as it is found:
var indexLookup = function(arr, search){
var index = -1, i, l;
for (i = 0, l = arr.length; i<l; i++) {
if (arr[i][0] === search) return arr[i][1];
}
return undefined;
}
You could then just use these functions like this in your code so that you don't have to have the looping in the middle of all your code:
var log = [
["comp","Please add company name!"],
["zip","Please add zip code!"]
];
objLookup(log, "zip"); // Please add zip code!
indexLookup(log, "comp"); // Please add company name!
Here is a jsfiddle that shows these in use.
Have you looked at jQuery's grep or inArray method?
See this discussion
Are there any jquery features to query multi-dimensional arrays in a similar fashion to the DOM?

Categories