I'm working in a real-time web application for my company, and I'm using MEAN stack with socket.io to provide real-time $scope variable updates in all clients in a specific socket room.
I'm currently using an array of objects that is dynamic, being able to add or remove elements, and I have to set a $watch function for each array element and pass the element's _id in it's respective $watch function. The problem is: AngularJS can't keep track of element's position in array by it's _id as elements are removed.
So I was considering, instead of using an array to store objects, use an object with unique properties that are each element's id, like this:
$scope.mainObj = {
'1234': {name: 'bob', age: 21},
'1235': {name: 'william', age: 23},
'1236': {name: 'rob', age: 28}
};
instead of:
$scope.mainArray = [
{_id: '1234', name: 'bob', age: 21},
{_id: '1235', name: 'william', age: 23},
{_id: '1236', name: 'rob', age: 28}
];
At first, I think this would remove the problem of having to index my array and having to locate the element's position in every client in a room to update a variable, making the changes atomic by updating the property named after element's _id.
Would this bring any performance issue or another problem? Like I said, array indexes would be dispensable, lookup functions to detect the element by _id wouldn't be necessary and the chances of updating the wrong element would be 0, as using the indexed array approach exists a minimal chance of the wrong element be updated when too many users are adding, removing or editing array elements.
So, is it a good idea? If not, how could I $watch array elements, pass the element _id in watch function and don't lose track of each element? What I have now is this:
$scope.setWatch = function (element, id) {
$scope.$watch(x, function() {
console.log('watching: ' + element);
console.log('do something with : ' + id);
});
}
socket.on('addArrayElm', function (res) {
var id = res.id;
$scope.mainArray.push(new arrayElement(id));//creates new element from constructor with received _id from server
$scope.mainArrayIndex.push(id);//push id in index array
var i = $scope.mainArrayIndex.length - 1;//get the pushed element index in array
var j = 'mainArrayIndex[' + i + '].name';//construct string to use in $watch function referring to the element in array's position 'i'
$scope.setWatch(j, z);
$scope.$apply();
});
The main problem is: if I remove an element, like the second one (index = 1), the $watch function is not destroyed, and as it took the index = 1 as a parameter, it's going to be triggered for the element that took index = 1, so the wrong _id is used.
What should I do?
Thanks!
Related
I have an array of type "User" and I'd like to delete all users that are 10 years old. My code :
struct User: Identifiable {
var id = UUID()
var name: String
var age: String
}
var array: User = [
User[name: "AZE", age: "10"]
User[name: "QSD", age: "37"]
]
What is the function for deleting an object from an element of that object? I hope you understood my problem and thank you for your answer.
You can use filter to keep only the elements of the array whose age property doesn't equal 10.
let filtered = array.filter { $0.age != "10" }
Unrelated to your question, but why is age a String? It should be an Int instead, since it represents a numeric value. Also, you should always make properties immutable (let) by default and only make them mutable (var) if they really need to be mutable.
i struggle my had now sine several days with a way to do conditional filter of an object array with another object array.
lack on capabilities to properly abstract here... maybe you ahve some ideas.
I have a given Object Array A but more complex
var ArrA = [{
number: 1,
name: "A"
}, {
number: 2,
name: "C"
}]
And i want to filer for all results matiching id of Object Array B
var ArrB = [{
id: 1,
categorie: "wine"
}, {
id: 3,
categorie: "beer"
}, {
id: 10,
categorie: "juice"
}]
And in the best case moving this directly also together with an if condition.... but i was not able to handle it ... here is where i am now ... which is not working....
let newArray = ArrA.filter{$0.number == ArrB.... }.
if (newArray.count != 0){
// Do something
}
is there a lean way to compare one attribute of every object in an array with one attribute of another every object in an array ?
Lets break this down: You need all arrA objects that matches arrB ids, so first thing first you need to map your arrB to a list of id (because you dont need the other infos)
let arrBid = Set(arrB.map({ $0.id })) // [1, 3, 10]
As commented below, casting it to Set will give you better results for huge arrays but is not mandatory though
Then you just need to filter your first arrA by only keeping object that id is contained into arrBid :
let arrAFilter = arrA.filter({ arrBid.contains($0.number) })
[(number: 1, name: "A")]
and voila
I have an object:
$scope.obj = {
name : "ok",
list : [{object},{object2}]
}
So, I have {object1}. How can I remove this object from list if I dont know key?
My code is:
var indexToDelete = list.people.keys(item);
console.log(indexToDelete);
delete list.people[indexToDelete];
Item is:
Object
$$hashKey:
"object:29"
artist:""
cover:""
song:"Basta 1"
source:""
type:"9"
I'm going to simplify your data structure just a bit, for clarity. I'm also going to assume that the $$hashKey can be used to determine whether the object to be removed is the same as one in the list -- if that's not the case, and we need to compare all the keys and parameters within the objects, the answer gets quite a bit more complex.
Given those assumptions, here is a vanilla javascript version that should work in all current browsers:
var list = [
{$$hashKey: 1, artist: "Alice"},
{$$hashKey: 42, artist: "Bob"},
{$$hashKey: 25, artist: "Charlie"}
];
var itemToRemove = {$$hashKey: 42, artist: "Bob"};
for (var i=0; i<list.length;i++) {
if (list[i].$$hashKey == itemToRemove.$$hashKey) {
list.splice(i,1); // removes the matched element
i = list.length; // break out of the loop. Not strictly necessary
}
}
console.log(list);
You could simplify that somewhat if itemToRemove is a reference to an object that is in the list; in that case you can just compare them directly instead of depending on $$hashKey:
var obj1 = {$$hashKey: 1, artist: "Alice"},
obj2 = {$$hashKey: 42, artist: "Bob"},
obj3 = {$$hashKey: 25, artist: "Charlie"};
var list = [obj1, obj2, obj3];
var itemToRemove = obj2;
for (var i=0; i<list.length;i++) {
if (list[i] === itemToRemove) {
list.splice(i,1); // removes the matched element
i = list.length; // break out of the loop. Not strictly necessary
}
}
console.log(list);
(If you are transpiling from ES6 there are quite a few new convenience methods available so that you don't need to iterate through the array manually: array.prototype.findIndex, array.prototype.filter for example, but these are not currently supported in enough browsers to be usable in production. Alternatively, if you are willing to add a library such as underscore.js, you could use _.without() to remove specific elements.)
Try array splice() method.
The splice() method changes the content of an array by removing existing elements and/or adding new elements.
Working demo :
var obj = {
name : "ok",
list : [
{"name":"abc"},
{"name":"xyz"}
]
}
obj.list.splice(0, 1);
console.log(obj);
There is two case
1. If you have already value as same as you want remove from array then do this
$scope.obj = {
name : "ok",
list : [{object},{object2}]
}
var index =$scope.obj.list.indexOf({object});
$scope.obj.list.splice(index,1);
2. If it is fix that you want to remove first element of array then do this
$scope.obj.list.splice(0,1);
You can make use of the delete keyword and then the property which needs to be deleted.
So, if you need to delete Object1, Firstly, using the findIndex method find the index in the list and then you can use delete.
var indexToDelete = $scope.obj.list.findIndex(YourCriteria);
delete $scope.obj.list[indexToDelete];
Note: findIndex is part of the ES7 Draft specification, so certain browsers might not support this method.
Edit1:
To bring more clarity, the findIndex method takes a callback which is called for each value in the Array.
I need length of books that added after item, here is my controller:
$scope.items = [
{"added_days_ago": 5, "books": [{"id":1, "added_days_ago": 6}, {"id":2, "added_days_ago": 3}, {"id":3, "added_days_ago": 4}]},
{"added_days_ago": 2, "books": [{"id":4, "added_days_ago": 3}]}
]
In controller i need to define filteredBooksLength (i using ng-table) and then show like:
{{filteredBooksLength}}
Something like:
filteredBooksLength = (books where book.added_days_ago > item.added_days_ago).length
JSFiddle
$scope.filteredBooksLength = function (item) {
return item.books.filter(function (book) {
return book.added_days_ago > item.added_days_ago
}).length
Should cover your needs - summon it within the ng-repeat block, and pass in the current item - filteredBooksLength(item)
jsFiddle
How it works
We're giving the scope access to a function that accepts an item object as an argument. We take the books array on that object and pass it though a filter function, which returns only those books where book.added_days_ago is greater than item.added_days_ago as an array. We then measure the length of that array and return it.
Note on style
From what you've written - and the way you've phrased your question - it looks like you've done some Python. It's accepted practice in JS to use camelCase rather than snake_case - you may wish to change that for the the keys in your items object. In addition, the "" around those keys are superfluous.
Keep at it!
I'd like to get some models in my collection that have the attribute unit. My current method involves this,
var unitIds = ciLocal.where({unit: !null});
console.log(unitIds.length);
The weird thing is that removing ! returns 58 (the total minus those that unit is not null) values while the code above returns 0.
Can anyone suggest a good way to loop my collection and return those models that have anything in unit?
Its probably worth mentioning that unit contains two values, one being unitID and the other being an array of more values. I need to get the whole model back and not just the unit section.
In this screenshot you can see that 68 has null while 69 has object.
{"carID":"37","unit":{"unitID":"37_Chaffinch_75","positionHistory":[{"lat":"51.474312","long":"-0.491672","time":"2011-07-08 11:24:47","status":"1","estimatedSpeed":"0","lastSoundFileName":"Car park Exit","lastSoundRange":"10","lastSoundTime":"2011-07-08 11:25:03","isToday":false,"minutesAgo":1028188}]},"registration":"CJ-361-YG","color":"Luxor","phone":"","model":"SDV8"}
You can use _.filter on your collection to specify a custom validation function.
filter _.filter(list, iterator, [context])
Looks through each value in the list, returning an array of all the values that pass
a truth test (iterator).
Something like this should keep the models with a defined, non null value
var c = new Backbone.Collection([
{id: 1, unit: 1},
{id: 2, unit: null},
{id: 3}
]);
c.filter(function(model) {
var v = model.get('unit');
return ((typeof(v)!=='undefined') && (v!==null));
})
And a demo http://jsfiddle.net/nikoshr/84L2R/