AngularJS Scope watch to find which binded attribute changed - angularjs

I am writing a program in which I watch an array inside of my scope. Scope.watch works as needed, which gives me the modified value. But, how do I find out which element has been changed?
for(var i=0; i<5; i++) {
myarray[i] = "var_" + i;
}
Scope.$watch(myarray[i], function (newValue,OldValue) {
alert(myarray[i]); // Doesn't give me the correct value changed attributes because i has been modified
});
How can I known which attributes have been changed, for example: var_1, var_2?

If your code really looks like this:
for(var i=0;i<5;i++)
{
myarray[i] = "var_" + i;
}
Scope.$watch(myarray[i], function (newValue,OldValue) {
alert(myarray[i])
}
Then the answer is simple. Your watch will always trigger when var_4 changes.
Why is that?
It's because you're creating the watch outside of the scope of the for loop. Because of the way variable scoping works in JavaScript, the loop variable is scoped to the function, so your code still works, but doesn't do what you would expect -- the value of i in Scope.$watch(myarray[i], ...) will be the last value from the loop.
If you want to specify the scope variables to watch within a loop, you'd do something like this:
for(var i=0;i<5;i++)
{
myarray[i] = "var_" + i;
Scope.$watch(myarray[i], function (varName) {
return function (newValue,OldValue) {
alert(varName + "changed, new value: " + newValue);
}
}("var_" + i));
}
This will create a new watch for each loop iteration, and passing the variable name through the intermediate function scope will ensure that it will be the correct one.
Having said all that, I'm fairly sure that you're doing it wrong if you find yourself needing to create watches in a loop. If you were to explain what you are trying to accomplish, we might be able to provide an answer that makes more sense. For instance, if you want to watch scope variables var_1 and var_2, you could simply:
Scope.$watch("var_1", function (newValue,OldValue) {
alert("var_1 changed: " + myarray[i])
}
Scope.$watch("var_2", function (newValue,OldValue) {
alert("var_2 changed: " + myarray[i])
}
If you need to observe changes to an array of arbitrary length and know the changed index, you can try this:
function firstDifferetIndex(a1, a2) {
for (var i = 0; i < a1.length; i++) {
if (a1[i] != a2[i]) {
return i;
}
}
return -1;
}
Scope.$watchCollection("[" + myarray.join(",") + "]" , function (new, old) {
var changedIndex = firstDifferentIndex(new, old);
alert("var_" + changedIndex + " changed");
});
I'm still fairly sure this is not what you want to actually do, but given the amount of information we've got on the problem, this is the best I can do. But I'm guessing you might want to combine ng-repeat with a custom directive to get what you want in a cleaner way.

You could use a hack: Demo
scope.myArray = [];
for(var i=0; i<5; i++) {
scope.myArray[i] = { text: "var_" + i, id: i };
scope.$watch("myArray." + i, function (new, old) {
console.log("Changed Variable", scope.myArray[new.id].text);
console.log("Index", old.id); // Or new.id as long as you don't modify them.
}, true);
}
So, if you need to find out which element is changed, use old.id or new.id.

I have found the solution for this $scope.$$watcher contain all the attribute for which the scope is watching . Iterating the $$watcher and checking the last value on it ($watch.last) and then you have name of the attribute in eval .So comparing the newvalue with the $watch.last attribute and fetching the name of the attribute from watch do the trick.
I will soon edit this answer with the example : Code

Related

For loop not being processed in array order [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 4 years ago.
The community reviewed whether to reopen this question 3 months ago and left it closed:
Duplicate This question has been answered, is not unique, and doesn’t differentiate itself from another question.
I am running an event loop of the following form:
var i;
var j = 10;
for (i = 0; i < j; i++) {
asynchronousProcess(callbackFunction() {
alert(i);
});
}
I am trying to display a series of alerts showing the numbers 0 through 10. The problem is that by the time the callback function is triggered, the loop has already gone through a few iterations and it displays a higher value of i. Any recommendations on how to fix this?
The for loop runs immediately to completion while all your asynchronous operations are started. When they complete some time in the future and call their callbacks, the value of your loop index variable i will be at its last value for all the callbacks.
This is because the for loop does not wait for an asynchronous operation to complete before continuing on to the next iteration of the loop and because the async callbacks are called some time in the future. Thus, the loop completes its iterations and THEN the callbacks get called when those async operations finish. As such, the loop index is "done" and sitting at its final value for all the callbacks.
To work around this, you have to uniquely save the loop index separately for each callback. In Javascript, the way to do that is to capture it in a function closure. That can either be done be creating an inline function closure specifically for this purpose (first example shown below) or you can create an external function that you pass the index to and let it maintain the index uniquely for you (second example shown below).
As of 2016, if you have a fully up-to-spec ES6 implementation of Javascript, you can also use let to define the for loop variable and it will be uniquely defined for each iteration of the for loop (third implementation below). But, note this is a late implementation feature in ES6 implementations so you have to make sure your execution environment supports that option.
Use .forEach() to iterate since it creates its own function closure
someArray.forEach(function(item, i) {
asynchronousProcess(function(item) {
console.log(i);
});
});
Create Your Own Function Closure Using an IIFE
var j = 10;
for (var i = 0; i < j; i++) {
(function(cntr) {
// here the value of i was passed into as the argument cntr
// and will be captured in this function closure so each
// iteration of the loop can have it's own value
asynchronousProcess(function() {
console.log(cntr);
});
})(i);
}
Create or Modify External Function and Pass it the Variable
If you can modify the asynchronousProcess() function, then you could just pass the value in there and have the asynchronousProcess() function the cntr back to the callback like this:
var j = 10;
for (var i = 0; i < j; i++) {
asynchronousProcess(i, function(cntr) {
console.log(cntr);
});
}
Use ES6 let
If you have a Javascript execution environment that fully supports ES6, you can use let in your for loop like this:
const j = 10;
for (let i = 0; i < j; i++) {
asynchronousProcess(function() {
console.log(i);
});
}
let declared in a for loop declaration like this will create a unique value of i for each invocation of the loop (which is what you want).
Serializing with promises and async/await
If your async function returns a promise, and you want to serialize your async operations to run one after another instead of in parallel and you're running in a modern environment that supports async and await, then you have more options.
async function someFunction() {
const j = 10;
for (let i = 0; i < j; i++) {
// wait for the promise to resolve before advancing the for loop
await asynchronousProcess();
console.log(i);
}
}
This will make sure that only one call to asynchronousProcess() is in flight at a time and the for loop won't even advance until each one is done. This is different than the previous schemes that all ran your asynchronous operations in parallel so it depends entirely upon which design you want. Note: await works with a promise so your function has to return a promise that is resolved/rejected when the asynchronous operation is complete. Also, note that in order to use await, the containing function must be declared async.
Run asynchronous operations in parallel and use Promise.all() to collect results in order
function someFunction() {
let promises = [];
for (let i = 0; i < 10; i++) {
promises.push(asynchonousProcessThatReturnsPromise());
}
return Promise.all(promises);
}
someFunction().then(results => {
// array of results in order here
console.log(results);
}).catch(err => {
console.log(err);
});
async await is here
(ES7), so you can do this kind of things very easily now.
var i;
var j = 10;
for (i = 0; i < j; i++) {
await asycronouseProcess();
alert(i);
}
Remember, this works only if asycronouseProcess is returning a Promise
If asycronouseProcess is not in your control then you can make it return a Promise by yourself like this
function asyncProcess() {
return new Promise((resolve, reject) => {
asycronouseProcess(()=>{
resolve();
})
})
}
Then replace this line await asycronouseProcess(); by await asyncProcess();
Understanding Promises before even looking into async await is must
(Also read about support for async await)
Any recommendation on how to fix this?
Several. You can use bind:
for (i = 0; i < j; i++) {
asycronouseProcess(function (i) {
alert(i);
}.bind(null, i));
}
Or, if your browser supports let (it will be in the next ECMAScript version, however Firefox already supports it since a while) you could have:
for (i = 0; i < j; i++) {
let k = i;
asycronouseProcess(function() {
alert(k);
});
}
Or, you could do the job of bind manually (in case the browser doesn't support it, but I would say you can implement a shim in that case, it should be in the link above):
for (i = 0; i < j; i++) {
asycronouseProcess(function(i) {
return function () {
alert(i)
}
}(i));
}
I usually prefer let when I can use it (e.g. for Firefox add-on); otherwise bind or a custom currying function (that doesn't need a context object).
var i = 0;
var length = 10;
function for1() {
console.log(i);
for2();
}
function for2() {
if (i == length) {
return false;
}
setTimeout(function() {
i++;
for1();
}, 500);
}
for1();
Here is a sample functional approach to what is expected here.
ES2017: You can wrap the async code inside a function(say XHRPost) returning a promise( Async code inside the promise).
Then call the function(XHRPost) inside the for loop but with the magical Await keyword. :)
let http = new XMLHttpRequest();
let url = 'http://sumersin/forum.social.json';
function XHRpost(i) {
return new Promise(function(resolve) {
let params = 'id=nobot&%3Aoperation=social%3AcreateForumPost&subject=Demo' + i + '&message=Here%20is%20the%20Demo&_charset_=UTF-8';
http.open('POST', url, true);
http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
http.onreadystatechange = function() {
console.log("Done " + i + "<<<<>>>>>" + http.readyState);
if(http.readyState == 4){
console.log('SUCCESS :',i);
resolve();
}
}
http.send(params);
});
}
(async () => {
for (let i = 1; i < 5; i++) {
await XHRpost(i);
}
})();
JavaScript code runs on a single thread, so you cannot principally block to wait for the first loop iteration to complete before beginning the next without seriously impacting page usability.
The solution depends on what you really need. If the example is close to exactly what you need, #Simon's suggestion to pass i to your async process is a good one.

Google Active Script: using map in an array with 2 arguments

This works:
Suppose I want to use in an ArrayFormula() the indirect() function, which doesn't work. That is, supposed I type this in cell E1, =ArrayFormula(indirect(address(row(E:E),column(A:A)))), this will return the value of cell A1 in all the cells in column E. To circumvent this, I created the custom function below:
function retValue(cell){
if(cell.map) {
return cell.map(retValue);
} else {
var cellRang = SpreadsheetApp.getActive().getRange(cell);
return cellRang.getValue();
}
}
Now, when I enter this =ArrayFormula(retValue(address(row(E:E),column(A:A)))) in cell E1, each cell in column E will have the corresponding value of the cell in the same row of column A.
My problem:
What I need is to have a custom function that receives 2 arguments, like function retValue2(cell, anotherRange) but I only care if cell is an array, as anotherRange must be an array anyway. What happens is that when I iteratively call cell.map(retValue2) the argument anotherRange is lost and I'm not entirely sure how to go about this.
I tried to come up with this:
function retValue2(cell, anotherRange) {
if (cell.map) {
return cell.map(retValue2);
} else {
var range = SpreadsheetApp.getActive().getRange(anotherRange);
var nrRows = range.getNumRows();
var nrCols = range.getNumColumns();
return cell + ',' + nrRows + ',' + nrCols;
}
}
But it fails because anotherRange is not recognized inside the iteration I think. How do I solve this?
PS.: in the example that works, why exactly does it work at all? I understand that when I do this return cell.map(retValue); it will use my own function as the callback, which would return all the values in the array, but in the spreadsheet it shows only the one on the same row. What is the magic here?
EDIT:
My end goal is to create my own lookup function where I pass a search key and a 2-dimensional array (rows and columns) and then, it locates the coordinates of that key in the array.
Look here:
function retCoord(sKey, sIRange) {
try {
var key = SpreadsheetApp.getActive().getRange(sKey).getValue();
}
catch(e) {
var key = sKey;
}
var range = SpreadsheetApp.getActive().getRange(sIRange).getValues();
nbRow = range.length;
nbColumn = range[0].length;
for(var i = 0; i<nbRow; i++){
for(var j = 0; j<nbColumn; j++){
if(range[i][j] == key){
return i + ", " + j;
}
}
}
}
If in my spreadsheet I enter something like =retCoord("K4","A:L") it will search the content of cell K4 in my 2-dimensional array A:L and return where in the array the value is, 1, 2 for example. It also works if I use =retCoord(K4,"A:L") or =retCoord("term searched","A:L"), and in this latter case I enter directly the term searched. This works fine until I use it in an ArrayFormula().
First, instead of =retCoord("K4","A:L") I could very well use =retCoord(address(4, 11),"A:L") for instance and my .getRange() method would get the cell K4 just fine.
Now, here is the big problem. I want to use my function in an ArrayFormula(), and, positioning my cursor in O1 and hoping to search the items from column D in the columns E through L I want to pass as one of the inputs of address(), row(O:O), like this: =ArrayFormula(retCoord(address(row(O:O),4,4),"E:L")), meaning that for each row, a new address is passed. that is, in O1 cell, it should return the result of retCoord(D1,"E:L"), in O2 should be retCoord(D2,"E:L"), in O3 should be retCoord(D3,"E:L") and so on.
The issue happens because in my function, sKey is an array and if I try to use the same approach as my function retValue (here above in the This works: section) it fails because now, in retCoord, I have 2 inputs, and the introspection function calling from before fails because of the second input. Of course I'm missing something and there is always a better and more elegant way to approach a problem. But for now, can anyone help me with this one?
EDIT2:
I changed the code a little and it seems I moved forward but not quite yet. Check comment below for line indicated by (*):
function retCoord(sKey, sIRange) {
var key = '';
try {
key = SpreadsheetApp.getActive().getRange(sKey).getValue();
return key;
}
catch(e) {
if (sKey.map) {
var objKey = sKey.map(retCoord);
return objKey; // (*) <--- comments below
key = objKey;
} else {
key = sKey;
}
}
var range = SpreadsheetApp.getActive().getRange(sIRange).getValues();
nbRow = range.length;
nbColumn = range[0].length;
for(var i = 0; i<nbRow; i++){
for(var j = 0; j<nbColumn; j++){
if(range[i][j] == key){
return key + " = "+ i + ", " + j;
}
}
}
}
This (*) line I added only to see what was returning from the map. Surprisingly (in a way), it is an object with all the elements of that column and that is expected. What I didn't expect was that if I return that object to my spreadsheet cell, it brings back only the value of that specific cell (as I wanted but not really as expected). But the problem is I cannot use that object to compare against a string as it will expand and become something else and will never match. Look:
If I do return objKey in my custom function, over cell O1 it returns "a", in O2 it returns
"b", in O3 it returns "c" as expected because those are the values
of my cells D1, D2, D3 respectively.
If I do return "-> " + objKey
in in my custom function, instead of returning -> a, -> b, -> c in O1, O2, O3 respectively, it returns -> =A:A,a,b,c,d,e,f for all the cells in column O, which seems it did some type of objKey.toString() under the hood before concatenating with "-> "
Conclusion: how do I "coerce" the apparent result of objKey into string keeping the apparent result when you return the object without changing it? Simply put, I want the concatenation "some string" + to be equal to "some string" + "one string representing the value in that row instead of an object". In other words, what the hell is happening here? How does Google Sheets now that in that row, that element is the one representing the one I want? This is what I asked in the "PS." in the first part of this post.
You want to search all keys in the sKey column within the Range sIRange and note the position of each key within the range into the corresponding row in a destination column ?
This is how you can do it with Apps Script without formulas:
function retCoord(sKeyColumn, destinationColumn, sIRange) {
var key = sKeyColumn;
var range=SpreadsheetApp.getActive().getRange(sIRange);
var rangeValues = SpreadsheetApp.getActive().getRange(sIRange).getValues();
var nbRow = rangeValues.length;
var nbColumn = rangeValues[0].length;
var sKeyRange=SpreadsheetApp.getActive().getRange(sKeyColumn);
var destinationRange=SpreadsheetApp.getActive().getRange(destinationColumn);
var sKeyValues=sKeyRange.getValues();
for(var k=0;k<sKeyValues.length;k++){
for(var i = 1; i<=nbRow; i++){
for(var j = 1; j<=nbColumn; j++){
if(range.getCell(i, j).getValue() == sKeyValues[k][0]){
destinationRange.getCell(k+1, 1).setValue(range.getCell(i, j).getA1Notation());
}
}
}
}
}
Sample call:
function myFunction(){
retCoord('A1:A6','B1:B6','C1:J7');
}
retCoord('A:A','B:B','C:J'); would also work but would take very long, since the code would also loop through empty rows

Using protractor with loops

Loop index (i) is not what I'm expecting when I use Protractor within a loop.
Symptoms:
Failed: Index out of bound. Trying to access element at index:'x', but there are only 'x' elements
or
Index is static and always equal to the last value
My code
for (var i = 0; i < MAX; ++i) {
getPromise().then(function() {
someArray[i] // 'i' always takes the value of 'MAX'
})
}
For example:
var expected = ['expect1', 'expect2', 'expect3'];
var els = element.all(by.css('selector'));
for (var i = 0; i < expected.length; ++i) {
els.get(i).getText().then(function(text) {
expect(text).toEqual(expected[i]); // Error: `i` is always 3.
})
}
or
var els = element.all(by.css('selector'));
for (var i = 0; i < 3; ++i) {
els.get(i).getText().then(function(text) {
if (text === 'should click') {
els.get(i).click(); // fails with "Failed: Index out of bound. Trying to access element at index:3, but there are only 3 elements"
}
})
}
or
var els = element.all(by.css('selector'));
els.then(function(rawelements) {
for (var i = 0; i < rawelements.length; ++i) {
rawelements[i].getText().then(function(text) {
if (text === 'should click') {
rawelements[i].click(); // fails with "Failed: Index out of bound. Trying to access element at index:'rawelements.length', but there are only 'rawelements.length' elements"
}
})
}
})
The reason this is happening is because protractor uses promises.
Read https://github.com/angular/protractor/blob/master/docs/control-flow.md
Promises (i.e. element(by...), element.all(by...)) execute their then functions when the underlying value becomes ready. What this means is that all the promises are first scheduled and then the then functions are run as the results become ready.
When you run something like this:
for (var i = 0; i < 3; ++i) {
console.log('1) i is: ', i);
getPromise().then(function() {
console.log('2) i is: ', i);
someArray[i] // 'i' always takes the value of 3
})
}
console.log('* finished looping. i is: ', i);
What happens is that getPromise().then(function() {...}) returns immediately, before the promise is ready and without executing the function inside the then. So first the loop runs through 3 times, scheduling all the getPromise() calls. Then, as the promises resolve, the corresponding thens are run.
The console would look something like this:
1) i is: 0 // schedules first `getPromise()`
1) i is: 1 // schedules second `getPromise()`
1) i is: 2 // schedules third `getPromise()`
* finished looping. i is: 3
2) i is: 3 // first `then` function runs, but i is already 3 now.
2) i is: 3 // second `then` function runs, but i is already 3 now.
2) i is: 3 // third `then` function runs, but i is already 3 now.
So, how do you run protractor in loops?
The general solution is closure. See JavaScript closure inside loops – simple practical example
for (var i = 0; i < 3; ++i) {
console.log('1) i is: ', i);
var func = (function() {
var j = i;
return function() {
console.log('2) j is: ', j);
someArray[j] // 'j' takes the values of 0..2
}
})();
getPromise().then(func);
}
console.log('* finished looping. i is: ', i);
But this is not that nice to read. Fortunately, you can also use protractor functions filter(fn), get(i), first(), last(), and the fact that expect is patched to take promises, to deal with this.
Going back to the examples provided earlier. The first example can be rewritten as:
var expected = ['expect1', 'expect2', 'expect3'];
var els = element.all(by.css('selector'));
for (var i = 0; i < expected.length; ++i) {
expect(els.get(i).getText()).toEqual(expected[i]); // note, the i is no longer in a `then` function and take the correct values.
}
The second and third example can be rewritten as:
var els = element.all(by.css('selector'));
els.filter(function(elem) {
return elem.getText().then(function(text) {
return text === 'should click';
});
}).click();
// note here we first used a 'filter' to select the appropriate elements, and used the fact that actions like `click` can act on an array to click all matching elements. The result is that we can stop using a for loop altogether.
In other words, protractor has many ways to iterate or access element i so that you don't need to use for loops and i. But if you must use for loops and i, you can use the closure solution.
Hank did a great job on answering this.
I wanted to also note another quick and dirty way to handle this. Just move the promise stuff to some external function and pass it the index.
For example if you want to log all the list items on the page at their respective index (from ElementArrayFinder) you could do something like this:
var log_at_index = function (matcher, index) {
return $$(matcher).get(index).getText().then(function (item_txt) {
return console.log('item[' + index + '] = ' + item_txt);
});
};
var css_match = 'li';
it('should log all items found with their index and displayed text', function () {
$$(css_match).count().then(function (total) {
for(var i = 0; i < total; i++)
log_at_index(css_match, i); // move promises to external function
});
});
This comes in handy when you need to do some fast debugging & easy to tweak for your own use.
I am NOT arguing with the logic or wisdom of the far more learned people discussing above. I write to point out that in the current version of Protractor within a function declared as async, a for loop like the below (which I was writing in typeScript, incorporating flowLog from #hetznercloud/protractor-test-helper, though I believe console.log would also work here) acts like what one might naively expect.
let inputFields = await element.all(by.tagName('input'));
let i: number;
flowLog('count = '+ inputFields.length);
for (i=0; i < inputFields.length; i++){
flowLog(i+' '+await inputFields[i].getAttribute('id')+' '+await inputFields[i].getAttribute('value'));
}
producing output like
count = 44
0 7f7ac149-749f-47fd-a871-e989a5bd378e 1
1 7f7ac149-749f-47fd-a871-e989a5bd3781 2
2 7f7ac149-749f-47fd-a871-e989a5bd3782 3
3 7f7ac149-749f-47fd-a871-e989a5bd3783 4
4 7f7ac149-749f-47fd-a871-e989a5bd3784 5
5 7f7ac149-749f-47fd-a871-e989a5bd3785 6
...
42 7f7ac149-749f-47fd-a871-e989a5bd376a 1
43 7f7ac149-749f-47fd-a871-e989a5bd376b 2
As I understand it, the await is key here, forcing the array to be resolved up front (so count is right) and the awaits within the loop cause each promise to be resolved before i is allowed to be incremented.
My intent here is to give readers options, not to question the above.
The easier way for doing this these days
it('test case', async () => {
let elems = element.all(selector)
for (let i=0; i < await elems.count(); i++) {
console.log(await elems.get(i).getText())
}
});

JSON: How do I select an array inside another array?

I have the current JSON file:
[{"id":"1","images":[{"img_id":"1"},{"img_id":"2"},{"img_id":"3"}]},
[{"id":"2","images":[{"img_id":"1"},{"img_id":"2"},{"img_id":"3"}]}
How do I select the array with ID 1 and list every 'img_id' inside it, without repeating it for the other array?
edit 1:
I am trying to parse it like this but this code is erroneous:
$("#button").click(function() {
$.getJSON("../path/to/json", function(data) {
$.each(data[0].images.img_id, function(i,data){
var new_data ="<p src='path/to/folder/"+images.img_id+"'></p>";
$(new_data).appendTo("#htmlTag");
});
}); return false;
});
Much appreciated.
You have an array of objects, where each object contains another array of objects. I'm assuming the JSON structure you are using is:
var a = [{"id":"1","images":[{"img_id":"1"},{"img_id":"2"},{"img_id":"3"}]},
{"id":"2","images":[{"img_id":"1"},{"img_id":"2"},{"img_id":"3"}]}];
I'm also assuming you are using JavaScript.
PostEdit:
Your code is fairly close, I believe what I have below should work:
$("#button").click(function() {
$.getJSON("../path/to/json", function(data) {
$.each(data[0].images, function(i,data){
var new_data ="<p src='path/to/folder/" + data.img_id + "'></p>";
$(new_data).appendTo("#htmlTag");
});
});
return false;
});
All I did was change the first parameter to your each call from: data[0].images.img_id to: data[0].images. Also, I changed the declaration of new_data from:
var new_data ="<p src='path/to/folder/"+images.img_id+"'></p>";
to:
var new_data ="<p src='path/to/folder/"+data.img_id+"'></p>";
Note that the parameter "data" in the each callback function is simply the element in the array, while "i" is the index of that element in the array. Therefore, data is an object which looks like this:
{"img_id":1}
So, you can get the ID via data.img_id. Hope this helps.
I think you're talking about references. I don't think they are possible in JSON. In case you strictly need them and still want the readable serialization of your objects - I'd suggest you to look into YAML
I think there is a typo in your JSON...it seems like a bracket is missing at the end, and one has been added at the start of line 2. But, assuming you meant this (and are using Javascript):
var myJson = [{"id":"1","images":[{"img_id":"1"},{"img_id":"2"},{"img_id":"3"}]},
{"id":"2","images":[{"img_id":"1"},{"img_id":"2"},{"img_id":"3"}]}];
then you can access whatever you need just like a nested Javascript object. If you wanted to only access the object with ID equal to 1, and order is not guaranteed, you would have to iterate:
for(var i = 0; i < myJson.length; i++){
if(myJson[i].id === "1"){
var imgs = myJson[i].images;
for(var j = 0; j < imgs.length; j++){
//do what you want with imgs[j].img_id
}
}
}

How would I remove a "row" in an array depending on the value of an element?

Here's what I'm currently doing/trying to do to accomplish my goal. But it is not removing the "row" the way I would like it too.
So, I'm making an object, then pushing it into an array. And the adding to the array part works fine and just as I expect.
var nearProfileInfoObj:Object = new Object();
nearProfileInfoObj.type = "userInfo";
nearProfileInfoObj.dowhat = "add";
nearProfileInfoObj.userid = netConnection.nearID;
nearProfileInfoObj.username = username_input_txt.text;
nearProfileInfoObj.sex = sex_input_txt.selectedItem.toString();
nearProfileInfoObj.age = age_input_txt.selectedItem;
nearProfileInfoObj.location = location_input_txt.text;
nearProfileInfoObj.headline = headline_input_txt.text;
theArray.push(nearProfileInfoObj);
So after that later on I need to be able to remove that object from the array, and it's not working the way I'm expecting. I want to take a variable whoLeft and capture their ID and then look in the array for that particular ID in the userid part of the object and if its there DELETE that whole "row".
I know you can do a filter with an array collection but that doesnt actually delete it. I need to delete it because I may be adding the same value again later on.
whoLeft = theiruserIDVariable;
theArray.filter(userLeaving);
public function userLeaving(element:*, index:int, arr:Array):Boolean
{
if (element.userid == whoLeft)
{
return false;
}
else
{
return true;
}
}
But this doesnt seem to be deleting the whole row like it implies. Does anyone know what i'm doing wrong?
Instead of modifying the original array, the new filtered array is returned by the filter method. So you need to assign the returned array to theArray.
Try this
theArray = theArray.filter(userLeaving);
EDIT This turned out to be slower than for loop:
An alternative to the hand coded loop could be something like this:
theArray.every(searchAndDestroy);
public function searchAndDestroy(element:*, index:int, arr:Array):Boolean
{
if (element.userid == whoLeft)
{
arr.splice(index,1);
return false;
}
return true;
}
As far as I know, every() terminates the first time the test function returns false. So the question is: for a big list, which is faster, the for loop or the loop that every() does with the overhead of the test function call.
EDIT #2 But this was faster than a for loop for a test I ran on an array of a million Points:
for each(var element:Object in theArray)
{
if (element.userid==whoLeft)
{
theArray.splice(theArray.indexOf(element),1);
break;
}
}
I think this is what you're looking for:
for(var i:uint = 0, len:uint = theArray.length; i<len; i++)
{
if(thisArray[i].id == whoLeft.id)
{
thisArray.splice(i, 1);
break;
}
}
However, do you really need it in an Array because you could always use a Dictionary which would mean accessing it by id which would be a lot simpler to remove.

Resources