Merge loops with jQuery.extend: last value is always the same? - javascript

I have two arrays of "hashes". I need to merge them together so the end result is another array of hashes, except the number of entries is the product of the two arrays.
inhabitants = {}
idx = 0
for (i=0; i<persons.length; i++) {
person = persons[i];
for (j=0; j<houses.length; j++) {
house = houses[j];
console.log(house);
inhabitants[idx] = $.extend(person,house);
console.log(inhabitants[idx]);
idx++;
}
}
What I end up with is a silly number of entries that depends on the ordering of the parameters in the $.extend() line. And the "house" entry that is added is ALWAYS THE LAST ENTRY IN THE ARRAY.
Clearly this $.extend() from jQuery is not doing what I expect. Can anyone help?

$.extend(a, b) merges b into a, and returns a.
The return value you're using but it seems you were missing the fact that a is also being changed - you're currently assigning the same variable person to inhabitants[idx] each time.

I'm not really getting what you are trying to do, but anyway just a few hints. It's a good idea to place all the semicolons (here they're missing in the first and second line). Then I don't see why you are using an object instead of an array where you just have to push new items. Another thing is that in js it is faster to cache the lengths of the persons and houses collections. I will try to come up with code how I understood your task, maybe I'm getting it wrong:
var inhabitants = [],
idx = 0,
personCount = persons.length,
houseCount = houses.length,
person = {},
house = {},
i = 0,
j = 0;
for (i = 0; i < personCount; i++) {
person = persons[i];
for (j = 0; j < houseCount; j++) {
house = houses[j];
inhabitants.push($.extend({}, person, house);
}
}
So to me it seems like a database join between the two tables persons and houses. Is this what you meant?

Related

Javascript Nested Loop Pushing to Array

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;

javascript loop string comparison headache

console.log(previousCompetitors);
console.log(competitors);
if(data.isVisible) {
var moveIndexTo = [];
for(var q=0; q<competitors.length;q++) {
moveIndexTo.push(-1);
}
for(var i = 0; i<competitors.length; i++) {
for(var j = 0; j<previousCompetitors.length; j++) {
console.log(competitors[i].name);
console.log(previousCompetitors[j].name);
if(competitors[i].name === previousCompetitors[j].name) {
moveIndexTo[j]= i;
break;
}
}
}
console.log(moveIndexTo);
}
I'm slowly going insane trying to figure out what is happening here. I have an array of competitor data that updates in order. They are both arrays and I want to track the changes from the previous ordering.
I console.log the data and can see that the data order has been changed yet every single time the moveIndexTo array ends up being [0,1,2,3,4,5] implying that previousCompetitors an Competitors have equal order. How can they be changed between when I console.log them at the top of the code block to when I perform the string comparison?
Competitors and previousCompetitors take roughly the form
[{name:'name1'},{name:'name2'},{name:'name3'},{name:'name4'},{name:'name5'},{name:'name6'}]
with a lot more going on in each object. So If that was previousCompetitors then competitors would be something like
[{name:'name6'},{name:'name2'},{name:'name3'},{name:'name4'},{name:'name5'},{name:'name1'}].
Note the switch of name1 and name6. So I would expect moveIndexTo to be [5,1,2,3,4,0].
Just try this : moveIndexTo[i] = j;
fiddle at : https://jsfiddle.net/c9mbbpjj/

JS multidimensional array spacefield

i wanna generate a 3x3 field. I want to do this with JS, it shall be a web application.
All fields shall inital with false. But it seems so that my code is not working correctly, but i don't find my fault. The goal is, that every spacesector is accessible.
Thats my idea:
// define size
var esize = generateSpace(3);
}
space[i] = false is replacing the array with a single boolean value false, not filling in all the entries in array you just created. You need another loop to initialize all the elements of the array.
function generateSpace(x) {
var space = [];
for (var i = 0; i < x; i++) {
space[i] = [];
for (var j = 0; j < x; j++) {
space[i][j] = false;
}
}
return space;
}
Also, your for() loop condition was wrong, as you weren't initializing the last element of space. It should have been i < space.length.
And when it's done, it needs to return the array that it created.
Since I got somewhat bored and felt like messing around, you can also initialize your dataset as shown below:
function generateSpace(x) {
return Array.apply(null, Array(x)).map(function() {
return Array.apply(null, Array(x)).map(function() {
return false;
});
});
}
The other functions work equally well, but here's a fairly simply looking one using ES6 that works for any square grid:
function generateSpace(x) {
return Array(x).fill(Array(x).fill(false));
}

Counter array in Javascript

I am trying to make two arrays. the unique array can get the elements (no repeats) from the text array, and the counter one can count the frequency of each elements. but something is wrong with the counter one.
var unique_array=new Array();
var counter_array=new Array();
var unique=true;
for (i=0;i<text_array.length;i++){
if (unique_array.length==0){
unique_array.push(text_array[0]);
counter_array.push(1);
}
else if(unique_array.length>0&&unique_array.length<=text_array.length){
for (j=0; j<unique_array.length;j++){
if (text_array[i]==unique_array[j]){
counter_array[j]=counter_array[j]+1;// something wrong with the
alert(counter_array[j]);
var unique=false;
}
}
if (unique==true){
unique_array.push(text_array[i]);
counter_array.push[1];
}
unique=true;
}
You could also simplify the code down using a hashmap and some ES5 higher-order functions:
var text_array = ["a1","a1","a2","a3","a2","a4","a1","a5"];
var counts = {};
text_array.forEach(function(el) {
counts[el] = counts.hasOwnProperty(el) ? counts[el]+1 : 1;
});
var unique_array = Object.keys(counts);
var counter_array=unique_array.map(function(key) { return counts[key]; })
You can do this much more simply using an object. Let the values be the keys of an object, then just increment the count of each property as you go. At the end, you can get an array of the unique keys and their values:
var text_array = ['foo','bar','foo','fum','fum','foo'];
var i = text_array.length;
var obj = {};
while (i--) {
if (obj.hasOwnProperty(text_array[i])) {
obj[text_array[i]]++;
} else {
obj[text_array[i]] = 1;
}
}
console.log('Unique values: ' + Object.keys(obj)); // Unique values: foo,fum,bar
console.log('Value counts: ' + Object.keys(obj).map(function(v){return obj[v]})); // Value counts: 3,2,1
Note that the sorting of counts in the output is purely coincidental.
As Jasvir posted, you can make it pretty concise:
var obj = {};
text_array.forEach(function(v) {
obj.hasOwnProperty(v)? ++obj[v] : obj[v] = 1;
});
But the first example is a bit easier to digest.
I think the approach is what's making it difficult. A hash table / associative array would be much easier to work with.
With a hash table (an object {} in JS), you can store each word in a key and increment the value of the key when you encounter the word again. Then, at the end, just go through the hash table and gather up all the keys which have small values. Those are your unique words.
function get_unique_words(text_array) {
var hash_table, i, unique_words, keys;
hash_table = {};
for(i = 0; i < text_array.length; i++) {
if(hash_table[text_array[i]] === undefined) {
hash_table[text_array[i]] = 1;
} else {
hash_table[text_array[i]]++;
}
}
// go through the hash table and get all the unique words
unique_words = [];
keys = Object.keys(hash_table);
for(i = 0; i < keys.length; i++) {
if(hash_table[keys[i]] === 1) {
unique_words.push(keys[i]);
}
}
return unique_words.sort();
}
console.log(get_unique_words(
['blah', 'blah', 'blah', 'goose', 'duck',
'mountain', 'rock', 'paper', 'rock', 'scissors']
));
Some issues and suggestions :
Don't use var twice for the same variable.
Browsers deal with it ok, but for clarity you should only be declaring your variables once.
Always localize your loop counters - forgetting a var before your i and j will cause them to become global variables.
This is relevant when you have a page with lots of code - all global variables will show up in the debugger's watch list at all times, making it harder to debug your code.)
Use the array literal notation [] instead of the function form Array.
The function form is longer and it's easier to forget the new. It's also easier to read (IMO).
Use more whitespace (it won't bite), such as before and after an equals sign:
var x = 1;
// vs.
var x=1;
It makes the code easier to read and most people don't overdo it.
Indent your code when it's inside a block (e.g. function, if, else, while, for, etc.).
This makes it easier to read the control flow of the code and will help prevent bugs.
Use three equals signs (===) unless you are using loose equality on purpose.
This will help someone looking at your code later (probably yourself) understand better what the test is supposed to be testing.

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