Handling Duplicate objects in Javascript - javascript

I have a javascript method which handles removing from one select box to another.
The code runs as follows :
function moveAllOptions(formName, selectId1, selectId2) {
var sourceSelect = eval("document." + formName + "." + selectId1);
var destSelect = eval("document." + formName + "." + selectId2);
for (var i = 0; i < sourceSelect.length; i++) {
var optionText = sourceSelect.options[i].text;
var optionValue = sourceSelect.options[i].value;
for (var j = 0; j < destSelect.length; j++) {
if (destSelect.options[j].value == optionValue) {
destSelect.options[j].value = null;
}
}
}
}
But I found a problem like when it encounters duplicate values it is not deleting all the values .For eg: in the view source I have
value="139">Customer Service<
value="231">Customer Service<
value="231">Customer Service<
value="231">Customer Service<
value="231">Customer Service<
In this case it removes only two objects from my left hand box.
Is there any way to remove duplicate objects also.One way I could think is create an two array objects(Two Dimensional), pass all the values in left hand side to one array then iterate to another array and finally pass again to normal options.

Well, first of all the eval to hunt elements is horrible. There are far better ways of doing this starting with getElementById.
As to your actual problem, again there are simpler native ways to do this: the add and remove methods of the select object (reference). Try this method to start with.
function transferOptions(A, B)
{
var a = document.getElementById(A);
var b = document.getElementById(B);
while(a.options.length)
{
var x = a.options[0]; //store the value
a.remove(0); //remove it from the DOM
b.add(x); //reinsert it (adds to end of list)
}
}
transferOptions('select1','select2') //usage

for (var j = 0; j < destSelect.length; j++) {
if (destSelect.options[j].value == optionValue) {
destSelect.options[j].value = null;
}
}
Let's say the first item matched. You set destSelect.options[0].value to null, which effectively removes the first option from the list. Next you check destSelect.options[1], which is actually the third option in the original list. The second option has just become first, and occupies destSelect.options[0].
The way to avoid this problem is to go backwards, starting with the last item in the list and working up to the first.
for (var j = destSelect.length-1; j >=0; j--) {
if (destSelect.options[j].value == optionValue) {
destSelect.options[j].value = null;
}
}

Related

Array index out of bounds in Angular 2

I want to declare an Array with 4 dimensions, then loop some stuff with for() - and then the program breaks. Here is my code:
Typescript:
MoarInfo: any = [[[[]]]];
JavaScript:
constructor(){
for(var i = 0; i < this.AllDataInfo[this.KontoAktuellYearIndex][this.KontoAktuellMonth].length; i++){
for(var a = 0; a < this.AllDataInfo[this.KontoAktuellYearIndex][this.KontoAktuellMonth][i].length; a++){
for(var b = 0; b < this.AllDataInfo[this.KontoAktuellYearIndex][this.KontoAktuellMonth][i][a].length; b++){
this.MoarInfo[i][a][b][0] = this.AllDataInfo[this.KontoAktuellYearIndex][this.KontoAktuellMonth][i][a][b][0];
this.MoarInfo[i][a][b][1] = this.AllDataInfo[this.KontoAktuellYearIndex][this.KontoAktuellMonth][i][a][b][1];
this.MoarInfo[i][a][b][2] = 'DetailsSpan';
}
}
}
}
The Problem definitively lies at the MoarInfo[][][][] array. I tested my code without it, and it works fine. I tried the following possibilities for the Typescript array declaration as well:
Moarinfo: any[]; MoarInfo = []; MoarInfo = [[[[]]]]; MoarInfo: any[][][][] = [[[[]]]];
And in JavaScript, I tried to declare a new Array, and then push some elements on the MoarInfo array, with different functions (split, unshift, push, concat) and nothing worked.
What am I doing wrong?
Check the size of this.MoarInfo[i][a][b]. You are trying to get the value by index 0,1,2. Looks like its size is less than 2 which is causing this error.
if(this.MoarInfo[i][a][b].size > 0){
this.MoarInfo[i][a][b][0] = this.AllDataInfo[this.KontoAktuellYearIndex][this.KontoAktuellMonth][i][a][b][0];
}
if(this.MoarInfo[i][a][b].size > 1){
this.MoarInfo[i][a][b][1] = this.AllDataInfo[this.KontoAktuellYearIndex][this.KontoAktuellMonth][i][a][b][1];
}
if(this.MoarInfo[i][a][b].size > 2){
this.MoarInfo[i][a][b][2] = 'DetailsSpan';
}
Okay, I figured it out for myself. You have to set the elements of the dimensions from the array at first blank, then you can fill them with content. First I declared an Array in TypeScript like this AnArray = [];. Then I switched to JavaScript ( to the constructer() function ) and filled it with blank elements. I archieved this with this.AnArray.push();. If you want to set elements for the first dimension use push([]);, if you want an element for the 4th dimension, use push([[[]]]);. And you can set your content space like this push([[['E1',0,0,'E2']]]);. Now you can use follwing syntax:
alert( this.AnArray[0][0][0][3] ); //returns 'E2'
The complete code from my project now works fine and looks like this:
for(var i = 0; i < this.AllDataInfo[this.KontoAktuellYearIndex][this.KontoAktuellMonth].length; i++){
this.test.push([[[]]]);
for(var a = 0; a < this.AllDataInfo[this.KontoAktuellYearIndex][this.KontoAktuellMonth][i].length; a++){
this.test[i].push([[]]);
for(var b = 0; b < this.AllDataInfo[this.KontoAktuellYearIndex][this.KontoAktuellMonth][i][a].length; b++){
this.test[i][a].push(['',0,'']);
this.test[i][a][b][0] = this.AllDataInfo[this.KontoAktuellYearIndex][this.KontoAktuellMonth][i][a][b][0];
this.test[i][a][b][1] = this.AllDataInfo[this.KontoAktuellYearIndex][this.KontoAktuellMonth][i][a][b][1];
this.test[i][a][b][2] = 'DetailsSpan';
}
}
}
I wonder if there is a better way than using arrays, but if you want it too, you can do it like this.
cheers

Using .push stops for loop from executing

I have a for loop that suddenly stops working when I try to push to an array. The best way to describe what's going on is just to show my code and try an explain what's going on.
for (var i = 0; i < childs.length; i++) {
if (childs[i].length > 0) {
for (var j = 0; j < amountsValue[i].options.custValues.length; j++) {
var label = amountsValue[i].options.custValues[j].label;
var value = amountsValue[i].options.custValues[j].value;
for (var k = childs[i].length - 1; k >= 0; k--) {
if (childs[i][k].attributes[label] != value) {
childBackup.push(childs[i][k]);
childs[i].splice(k, 1);
}
}
}
amountsValue[i].id = childs[i][0].attributes.internalid;
childs.push(childBackup);
}
}
What's happening is I am looping through an array of items which may or may not have custom options available such as different sizes or colours. The loop will check to see if there are any then get the value and label from the array.
After this, we then loop again to try and match up the values with option values stored within a separate model. The plan is to check if the value is the same as the one stored and if not then splice it from the array. The process of elimination should eventually leave only one option left and that will be used to get the internalid.
During this a back up of the spliced objects is kept so that they can be appended to the array again so that the user can change the option they want. The problem is using childs.push(childBackup) stops the browser form reading the options on amountsValue. This works if the code is removed or it is pushed into another index so I'm really not sure why it isn't working.
Does anyone have any suggestions on how to get this working? I'm sorry if this doesn't make much sense, I've tried to explain it as best I can but let me know if anything needs to be cleared up.
EDIT: I have fixed the issue. Thank you to everyone who suggested ways to solve the problem. As others said, I was trying to manipulate the array I was looping through and changing the length on it. So that part of the code was taken outside the loop and after the initial loop another loop was set up which contained the following code:
for (var i = 0; i < childBackup.length; i++) {
childs[0].push(childBackup[i]);
}
It now works as intended. Thank you.
You are manipulating the array you are looping through.
var count = childs.length;
for (var i = 0; i < count; i++) {
if (childs[i].length > 0) {
for (var j = 0; j < amountsValue[i].options.custValues.length; j++) {
var label = amountsValue[i].options.custValues[j].label;
var value = amountsValue[i].options.custValues[j].value;
for (var k = childs[i].length - 1; k >= 0; k--) {
if (childs[i][k].attributes[label] != value) {
childBackup.push(childs[i][k]);
childs[i].splice(k, 1);
}
}
}
amountsValue[i].id = childs[i][0].attributes.internalid;
childs.push(childBackup);
}
}

Find difference in two arrays

I have two arrays of email addresses and I am trying to find the difference in the two. One of the arrays contains current email address. The other contains current group members email address. I am updating the group list with the current email addresses and removing the address in the group that are not in the current email addresses. I cannot wrap my head around how I get my for loop to accomplish this. Here is my code so far...
for(i = 0; i < GROUP_USERS.length; i++){
var currentMember = GROUP_USERS[i];
for(x = 0; x < DOMAIN_USERS.length; x++){
if(DOMAIN_USERS[x] != currentMember){
continue;
} else {
}
}
It seems like I need to test the end of my loop, or something.
EDIT
I am using Google Apps Script (SDK). I will have to push all of the emails that need to be deleted to an array and then use the GroupApps class to remove those emails from the group. Then I will need to push the DOMAIN_USERS email address that do not already reside in the group, to the group. So, essentially, I will have two arrays. One array of emails that need to be removed from the group and one array of emails that need to be added to the group. Hopefully, that makes more sense.
You need create another variable to check currentMember exists in DOMAIN_USERS array
after that you can remove it from GROUP_USERS array
for (i = 0; i < GROUP_USERS.length; i++) {
var currentMember = GROUP_USERS[i];
var isContain = false;
for (x = 0; x < DOMAIN_USERS.length; x++) {
if (DOMAIN_USERS[x] == currentMember) {
isContain = true;
}
}
if (!isContain) {
emailTobeRemove.pop(currentMember);
i--;
}
}
Unless I'm misunderstanding the logic the same result can be reached with
GROUP_USERS = DOMAIN_USERS;
If I understand correctly you want to remove all emailaddresses from GROUP_USERS that aren't in DOMAIN_USERS, then add the emailaddresses from DOMAIN_USERS that aren't in GROUP_USERS to the GROUP_USERS array, correct?
You could first make a 'contains' function (for compatability you could use this instead of indexOf()):
function contains(arr, val) {
for (var i = 0; i < arr.length; i++) {
if (arr[i] === val) {
return true;
}
}
return false;
}
Then to delete all emailaddresses from GROUP_USERS that aren't in DOMAIN_USERS. in a for loop:
for(i=0;i < GROUP_USERS.length; i++) {
if(!contains(DOMAIN_USERS, GROUP_USERS[i])) {
GROUP_USERS.splice(i, 1);
}
}
Then another for-loop to add the ones from DOMAIN_USERS to GROUP_USERS:
for(i=0; i < DOMAIN_USERS.length; i++) {
if(!contains(GROUP_USERS, DOMAIN_USERS[i])) {
GROUP_USERS.push(DOMAIN_USERS[i]);
}
}
if you are looking for difference in two arrays
use grip and inarray
demo
The 2 lists you need are the relative complement of GROUP_USERS in DOMAIN_USERS and of DOMAIN_USERS in GROUP_USERS. So define a function that finds all the members of an array a that are not in a second array b, and then use that to find which emails need to be added and deleted.
function relComp(a, b) {
var r = [];
for (var i = 0; i < a.length; i++) {
if (b.indexOf(a[i]) == -1) {
r.push(a[i]);
}
}
return r;
}
var toDelete = relComp(GROUP_USERS, DOMAIN_USERS);
var toAdd = relComp(DOMAIN_USERS, GROUP_USERS);
relComp can also be written with a filter:
function relComp(a, b) {
return a.filter(function(item) {
return b.indexOf(item) == -1;
});
}
JSFiddle
Its very easy using open source project jinqJs
See Fiddle Example
//Use jsJinq.com open source library
var current = ["a#yahoo.com", "b#yahoo.com", "c#yahoo.com", "d#yahoo.com"];
var group = ["a#yahoo.com", "d#yahoo.com", "yyy#yahoo.com", "xxx#yahoo.com"];
var currentComplex = [{email:"a#yahoo.com"},{email: "b#yahoo.com"}, {email:"c#yahoo.com"}, {email:"d#yahoo.com"}];
//Gets emails that are in current not in group
var result = jinqJs().from(current).not().in(group).select();
var result2 = jinqJs().from(currentComplex).not().in(group, 'email').select();

using for loop and if statement to check id names with numbers

for (var i = 1; i < 81; i++){
if($(this).hasClass('member-'+i)){
('promote'+i) = true;
}
}
I have 80 droppable boxes. They each has an id called member-1, member-2, etc., when someone drags an item into the boxes, the variable will be turned to true and be passed to another function.
So far I found this is not working. I wasn't sure why. It is inside a droppable drop function.
since I have 80 boxes...I don't feel like typing them out manually.
Make promote an array, rather than 80 different variables. Then you can do:
var promote = [];
for (var i = 1; i < 81; i++){
if($(this).hasClass('member-'+i)){
promote[i] = true;
}
}
Much better would be to just see what classes do exist rather than testing for 81 different classes:
var matches, promotes = [], cls = this.className;
var regex = /member-(\d+)/g;
while (matches = regex.exec(cls)) {
// matches[1] contains the number from the member-xx class name
promotes.push(parseInt(matches[1], 10));
}
// promotes is an array that contain a list of the member-xx numbers that exist
// on this object

Element sort routine works in Firefox, but crashes in Chrome

I've written the following routine to sort the option elements within a select:
function SortSelect(select)
{
var tmpAry = new Array();
for (var i in select.options)
tmpAry[i] = select.options[i];
tmpAry.sort(function(opta, optb)
{
if (opta.id > optb.id) return 1;
if (opta.id < optb.id) return -1;
return 0;
});
while (select.options.length > 0)
select.options[0] = null;
for (var i in tmpAry)
select.appendChild(tmpAry[i]);
}
It works just fine with Firefox as part of a Greasemonkey script. However, in Chrome, both with and without TamperMonkey, I get this:
Uncaught Error: NOT_FOUND_ERR: DOM Exception 8
SortSelect:125
As is typical for Javascript debuggers, the error line number is totally wrong, so it's difficult to pin down exactly why this is breaking and where. I'm open to suggestions as to why the code is bugging out or ways to effectively debug it (I'm new to Chrome). Thanks.
You should iterate over the options collection using an index, not a for..in loop. The following:
for (var i in select.options) {
tmpAry[i] = select.options[i];
should be:
var options = select.options;
for (var i=0, iLen=options.length; i<iLen; i++) {
tmpAry[i] = options[i];
}
You are likely getting properties from the options collection that aren't option elements, such as length.
You also should not assign "null" to an option. If you want to remove all the options, just set the length of options to zero:
var options.length = 0;
Finally, you should iterate over tmpAray using an index since for..in will not return the options in the same order in every browser and may return non-numeric enumerable properties if there are any, use an index. Also, you can just assign the option back to the select's options collection, there is no need for appendChild:
select.options.length = 0;
for (var i=0, iLen=tmpAry.length; i<iLen; i++) {
select.options[i] = tmpAry[i];
}
If you are not removing any options, you should be able to just assign them in the new order, but some browsers can't handle that so removing them first is best.
Edit
Note that while the options property of a select element is readonly, the properties of an options collection are not. You can assign values (which should be references to option elements) directly to them.
What are you trying to do with this piece of code?
while (select.options.length > 0)
select.options[0] = null;
If the answer is you're trying to clear all the select options, that seems dangerous. I could see how this could easily be an infinite loop.
It looks to me like this would be a lot safer:
for (var i = select.options.length - 1; i > 0; i--) {
select.remove(i);
}
Then, there's an select.options.add() method for adding them back.
FYI, it is also considered risky practice to use this construct on arrays or pseudo arrays:
for (var i in select.options)
for (var i in tmpAry)
as that can pick up properties that have been added to the object in addition to just array elements.
More typing, but safer to use:
for (var i = 0, len = tmpAry.length; i < len; i++) {
// code here
}
These lines can cause an infinite loop or an access violation:
while (select.options.length > 0)
select.options[0] = null;
Also, you do not need to delete the nodes and reinsert them; appendChild() works fine in all browsers to move nodes around.
So, this code will work and should be more efficient than removing and recreating nodes (which can also trash any event listeners). :
See it in action at jsFiddle.
function SortSelect (select)
{
var tmpAry = [];
for (var J = select.options.length - 1; J >= 0; --J)
tmpAry.push (select.options[J] );
tmpAry.sort ( function (opta, optb) {
if (opta.id > optb.id) return 1;
if (opta.id < optb.id) return -1;
return 0;
} );
while (tmpAry.length) {
select.appendChild ( document.getElementById (tmpAry[0].id) );
tmpAry.shift ();
}
}

Categories

Resources