I'm working on a calculation form that generates a multi-dimensional array with two values [selection], something like:
[[10,0.5], [18,0.75]]
The second array [lookup-table] contains a range of values:
["10","0.5","0.1"], ["12","0.5","1.1"], ["14","0.5","3.1"], ["16","0.5","5.1"],["18","0.5","7.1"], ["20","0.5","9.6"], ["22","0.5","11.6"]... ["18","0.75","9.1"]
I'm trying to match the index [0], [1] values in the [selection] array with the same index values in the [lookup-table] array.
["10","0.5","0.1"]... ["18","0.75","9.1"]
Once this is done, I'd like to retrieve the value for the index [x][2] value in the [lookup-table]:
["10","0.5","0.1"]... ["18","0.75", "9.1"]
I did find a very helpful and similar question here: JavaScript - Compare two multidimensional arrays
And in the console, it seems to correctly identify matches in the array index.
I'm using more or less a similar function:
function find(haystack, needles) {
var lookupValue = [];
//Iterate through all elements in first array
for(var x = 0; x < haystack.length; x++){
//Iterate through all elements in second array
for(var y = 0; y < needles.length; y++){
/*This causes us to compare all elements
in first array to each element in second array
Since haystack[x] stays fixed while needles[y] iterates through second array.
We compare the first two indexes of each array in conditional
*/
if(haystack[x][0] == needles[y][0] && haystack[x][1] == needles[y][1]){
console.log("match found");
console.log("Array 1 element with index " + x + " matches Array 2 element with index " + y);
//Retrieve the price for the high and low lookup values and store in array
lookupValue = haystack[x][2];
}
}
}
return lookupValue;
}
var totalResult = find(lookupTable, flushGrowthArray ).toString();
However when I try to retrieve the haystack[x][2] indexed value from the matches in the [lookup-table], for some reason I'm only returning a single value.
I think that it's probably a very simple mistake; what am I doing wrong? I appreciate any new insights.
lookupValue = haystack[x][2];
should be:
lookupValue.push(haystack[x][2]);
so you add the match to the array.
Related
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)
Good evening, I attempting to detect duplicate characters in a string. More specifically, I am trying to find up to two different duplicates within an Array. If there is one duplicate, add a sub-string, and if there is another duplicate, add a different sub-string. Is there any way to do this?
Here is some example code I have so far:
var CodeFieldArray = ["Z80.0", "Z80.1", "Z80.0", "Z70.4"];
/* We have an array here used to create the final string at the end of the
code. It is a dummy array with similar variables in my actual code. For
reference sake, there may be only one object in the array, or 7 total,
depending on the user's input, which is where the duplicate detection should
come in, in case the user enters in multiples of the same code. */
var i, Index;
for (i = 0, L = 0; i < CodeFieldArray.length; i++) {
Index = CodeFieldArray[i].indexOf(CodeFieldArray[i]);
if(Index > -1) L += 1;
Extra0 = CodeFieldArray.indexOf("Z80.8");
Extra1 = CodeFieldArray.indexOf("Z80.9");
if(L >= 2 && Extra0 == -1) CodeFieldArray.push("Z80.8");
Extra0 = CodeFieldArray.indexOf("Z80.8");
if(L >= 4 && Extra0 != -1 && Extra1 == -1) CodeFieldArray.push("Z80.9");
console.println(Extra0);
}
/*^ we attempted to create arguments where if there are duplicates
'detected', it will push, "Z80.8" or, "Z80.9" to the end of the Array. They
get added, but only when there are enough objects in the Array... it is not
actually detecting for duplicates within the Array itself^*/
function UniqueCode(value, index, self) {
return self.indexOf(value) === index;
}
CodeFieldArray = CodeFieldArray.filter(UniqueCode);
FamilyCodes.value = CodeFieldArray.join(", ");
/* this is where we turn the Array into a string, separated by commas. The expected output would be "Z80.0, Z80.1, Z70.4, Z80.8"*/
I have it to where it will add "Z80.8" or "z80.9" if they are not present, but they are being added, only if there are enough objects in the Array. My for-loop isn't detecting specifically the duplicates themselves. If there was a way to detect specifically the duplicates, and create an argument based off of that, then we would be doing grand. The expected output would be "Z80.0, Z80.1, Z70.4, Z80.8"
You can use Set and forEach and includes
var CodeFieldArray = ["Z80.0", "Z80.1", "Z80.0", "Z70.4"];
let unique = [...new Set(CodeFieldArray)];
let match = ['Z80.8','Z80.9'];
let numOfDup = CodeFieldArray.length - unique.length;
if(numOfDup){
match.forEach(e=>{
if(!unique.includes(e) && numOfDup){
unique.push(e);
numOfDup--;
}
})
}
console.log(unique.join(','))
So the idea is
Use Set to get unique values.
Now see the difference between length of original array and Set to get number of duplicates.
Now will loop through match array and each time we push item from match array into unique we reduce numOfDup by so ( to handle case where we have only one duplicate or no duplicate ).
In the end join by ,
You could do something like this:
var uniqueArray = function(arrArg) {
return arrArg.filter(function(elem, pos,arr) {
return arr.indexOf(elem) == pos;
});
};
uniqueArray ( CodeFieldArray )
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;
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.
Im still needing help with this, and have edited the jsfiddle post to show my problem. http://jsfiddle.net/7ztEf/6/
I want to return number to associated index value [0] =0 [1]=1 as you can see the index string returns all numbers. Thanks again Paul
I have a number generator script that returns values to DIV ID's. I need to hook into this somehow, to enable replacing color based upon the number value i.e. > 1 && <= 20 = red etc.
function myNumbers(numbers, type) {
for (var x in numbers) {
document.getElementById(type + x).innerHTML = numbers[x];
}
}
This script fills each of the DIVs named num0 ... num3 with a random number.
I have managed to query the first value of numbers[x] but need to set an index order to loop through the rest, or something.
Use Array.forEach.
numbers.forEach(function (number, index) {...})
Don't use for..in for arrays. They're meant to be used on objects so using for..in on arrays will return such things as the length element.
Either use forEach as ethagnawl mentioned or use the traditional for loop:
for (var x=0; x < numbers.length; x++) {
document.getElementById(type + x).innerHTML = numbers[x];
}