I'm using Angular. I'm trying to compare two arrays of objects
I was able to get it working doing it like this:
var compareUsers = function () {
//Comparing assigned groups with all to return available users
var assignedUsersIds = {};
var usersIds = {};
availableUsers = []; //declared higher up, populated with user objects on load
//assignedUsers, declaired higher up, populated by function calling compareUsers
assignedUsers.forEach(function (el, i) {
assignedUsersIds[el.id] = assignedUsers[i];
});
allUsers.forEach(function (el, i) {
usersIds[el.id] = allUsers[i];
});
for (var i in usersIds) {
if (!assignedUsersIds.hasOwnProperty(i)) {
availableUsers.push(usersIds[i]);
}
};
console.log(availableUsers);
return availableUsers;
}
I found a better way to do it so I refactored it to this, using lodash:
var compareUsers = function () {
availableUsers = _.filter(allUsers, function(user){
return !_.findWhere(assignedUsers, user);
});
console.info(availableUsers);
return availableUsers;
}
However, I'm not getting the correct results and not sure what I messed up. The new methods returns availableUsers which are in the assignedUsers list for some groups. The first time it runs, it seems to work but if I keep changing what group i'm looking at the results are all off and don't add up.
I found this method here.
In the first example you are using Array.push, which is always adding up (obviously), no matter how often you call compareUsers.
In the second example you are overwriting availableUsers each time you are calling compareUsers with the result of _.filter
I'd suggest that instead of doing:
availableUsers = _.filter(allUsers, function(user){
return !_.findWhere(assignedUsers, user);
});
you do:
availableUsers = availableUsers.concat(_.filter(allUsers, function(user){
return !_.findWhere(assignedUsers, user);
}));
This should work. It will concat the availableUsers array with the result of _.filter.
Related
I'm trying to create an array of objects using an array of objects.
My first array is like that :
And I want to create an object list with only an id, a name and a task.
This is what i do actually, but it doesn't work :
var lists = data.filter(l => {
return new ListModel(l.listId, l.listName, 'todo');
});
The ListModel object is :
class ListModel {
constructor(id, name, tasks) {
this.id = id;
this.name = name;
this.tasks = tasks;
}
setId(id) {
this.id = id;
}
setName(name) {
this.name = name;
}
setTask(task) {
this.task = task;
}
}
The filter() function is more-so utilized for returning an array based upon some search criteria, similar to a WHERE clause. What you want is to utilize is the map() function using something like this:
var lists = data.map(l => {
return new ListModel(l.listId, l.listName, 'todo');
});
Use .map instead of .filter:
var lists = data.map(l => {
return new ListModel(l.listId, l.listName, 'todo');
});
filter is for filtering elements, and the return value is taken as a boolean value for this. So in your case all elements will be validated because a new object is allways a truthy value, and you'll get a equal array.
Edit your question with the original array in text format and I'll create a working example for you.
I think you want the map() function: https://www.w3schools.com/jsref/jsref_map.asp
Something like this:
const newData = data.map( item => {
return {
item.listId,
item.listName,
'todo',
}
})
Use map instead of filter.
Filter creates a new array with all elements that pass the test implemented by the provided function/expression.
You're currently using Array.prototype.filter(), which removes non-matching elements from the current array. What you want to do, as far as I can tell, is use Array.prototype.map() to create a new array based upon the ListModel object. Here's how you'd do it:
var lists = data.map(l => new ListModel(l.listId, l.listName, "todo"));
I am trying to port some of my Firebase database calls to an IOT board that does not have jQuery, just good old JavaScript.
In my code I originally had a jQuery $.each(tripData, function(index, element)
... loop to iterate through my results.
I have switched this to:
var tripsRef;
tripsRef = firebase.database().ref('trips/');
tripsRef.orderByChild('timestamp').limitToLast(100).on('value', function (response) {
var tripData = response.val();
tripData.forEach(function (index, element) {
if (element.status >= 0) {
var trip = new Object();
trip.id = index;
trip.launch = element.launch;
trip.status = element.status;
}
});
... but, I am getting the following error:
forEach is not a function
I am not sure how to resolve this.
for(let index in tripData){
element = trimpData[index];
}
not realy foreach, but works exactly like it
but you also can use map functions
You should really figure out if your response is an Array or Object.
$.each() iterates over arrays AND objects, thats why it works.
you should use for...in statement if you really want to iterate over this object tripData.
for(let prop in tripData)
{
if (tripData.hasOwnProperty(index))
{
item = tripData[prop];
// do stuff
}
}
lear about for...in statement here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in
While the answer by #TypedSource will iterate over the resulting children, the order in which it iterates is undetermined - and very likely not going to be by timestamp.
A snapshot that you get as the result for a query contains three pieces of information for each child: its key, its value, and its position relative to the other children. When you call .val() on the snapshot, you lose the relative ordering.
To maintain the order, use the built-in forEach() method of the snapshot:
var tripsRef;
tripsRef = firebase.database().ref('trips/');
tripsRef.orderByChild('timestamp').limitToLast(100).on('value', function (response) {
var index = 0;
response.forEach(function (child) {
var element = child.val();
if (element.status >= 0) {
var trip = new Object();
trip.id = index;
trip.launch = element.launch;
trip.status = element.status;
}
index++;
});
});
Array.of(response.val()).forEach should work if it is just an array-like object missing its iterator
I want to create a similar construction in my code.
var inList = findItem(list, data);
if(!inList) {
var item = inList.item;
}
function findItem(list, data) {
var item = list.find("[data-day='"+data.day+"']")
// more code.
// conditional return
return {item: item, valueOf:function(){return false}};
}
But it doesn't work because overwriting valueOf doesn't play nicely with a simple truthfull check (in the way that I want it to work).
and having code like if(inList == false){} looks less clean imo. Is there a way to make this work?
Boolean checks don't invoke valueOf - all objects are considered truthy. If you want to circumvent that, you'll have to invoke it yourself explicitly:
if (!inList.valueOf()) …
You should not depend on code that uses valueOf,
if you wanted to do something where you are returning an object,
just add another property instead.
var findResult = findItem(list, data);
if(!findResult.found) {
var item = findResult.item;
}
function findItem(list, data) {
var item = list.find("[data-day='"+data.day+"']");
// more code.
// conditional return
return {item: item, found: false};
}
Then again, I forgot what I was doing 5 years ago.
I am implementing a cache function in a computed observable.
Is there any way to invalidate the cache below if the items collection differs since the last call?
I have seen examples of dirty checking where a serialized version of the observable is used to determine if the collection has changed, but it's too expensive for me, since there may be hundreds of items.
var itemCache;
var manipulatedItems = ko.pureComputed(function(){
var items = someObervable();
if(!itemCache /* || someObervable.hasChangedSinceLastCall */) {
itemCache = heavyWork(items);
}
return itemCache;
});
var heavyWork = function(items){
// do some heavy computing with items
return alteredItems;
};
In my viewmodel:
myViewModel.itemList = ko.pureComputed(function(){
var result = manipulatedItems();
return result;
});
Since computed observables always cache the last value, there's no reason to store it separately. In fact, storing it separately can cause trouble with getting the latest data in your application.
var manipulatedItems = ko.pureComputed(function () {
var items = someObervable();
return heavyWork(items);
});
var heavyWork = function (items) {
// do some heavy computing with items
return alteredItems;
};
Yes, but you need to use .subscribe and keep the relevant moments in vars inside your own closure. There is no "last-modified-moment" property on observables or inside ko utils to be found.
In your repro, you could do something like this:
var lastChange = new Date();
var lastCacheRebuild = null;
var itemCache;
someObervable.subscribe(function(newObsValue) { lastChange = new Date(); });
var manipulatedItems = ko.pureComputed(function(){
var items = someObervable();
if(!itemCache || !lastCacheRebuild || lastChange > lastCacheRebuild) {
lastCacheRebuild = new Date();
itemCache = heavyWork(items);
}
return itemCache;
});
As far as the repro is concerned you could even put the items = someObservable() bit inside the if block.
PS. This is not recursive, i.e. the subscription is only on someObservable itself, not on the observable properties of things inside that observable. You'd have to manually craft that, which is specific to the structure of someObservable.
I need to wait for two promises to resolve before doing something.The problem is that sometimes I can have only one promise to send based on what the client want, and I need to count on the results that comes from this $q.all. How can I dynamically define the vars?
var makePurchaseToResolve = [];
if( CartService.items.draws.length ) {
var drawOrder = {};
drawOrder.price = $scope.getTotal();
drawOrder.paymentMethodId = $scope.payment.paymetMethodId;
drawOrder.orderItems = CartService.items.draws;
drawOrder.payFromBalance = false;
makePurchaseToResolve.push(DrawService.makePurchase(drawOrder));
}
if( CartService.items.hunters.length ) {
var hunterOrder = {};
hunterOrder.paymentMethodId = $scope.payment.paymetMethodId;
hunterOrder.orderItems = CartService.items.hunters;
makePurchaseToResolve.push(HunterService.makePurchase(hunterOrder));
}
$q.all(makePurchaseToResolve).then(function( res ) {
$q.all([HunterService.getPurchase(res[0].data.data),DrawService.getPurchase(res.data.data)]).then(function() {
//here is the problem, because i dont know if i have the two promises to resolve or only one
$scope.hunters = res[0].data.data[0];
$scope.drawOrder = res[1].data.data;
})
}
A little known fact is that $q.all can also take an object rather than an array which can simplify your life in this case:
var purchases = {}; // an object rather than an array for named properties
if( CartService.items.draws.length ) {
//...
purchases.drawOrder = DrawService.makePurchase(drawOrder); // named assign
}
if( CartService.items.hunters.length ) {
//...
purchases.hunters = HunterService.makePurchase(hunterOrder);
}
// any more additions to the objects here..
$q.all(purchases).then(function(resolved){
// resolve anything further you need
$scope.purchases = resolved; // Add $scope.purchases.hunters etc
});
The promises are resolved in an object so they're named, instead of having an array you get a resolved object with properties so instead of doing arr[0] which might be out of order you'd do resolved.hunters or resolved.drawOrder. This solution just nests into $scope one level and does it automatically.
You can also keep the order of the promises, if you'd like, and pass in null for those that were not applied based on your condition.
This is an alternative for when your service gets a dynamic number of purchase requests from the caller and naming is not an option.
$q.all([null, huntersOrderPromise])
.then(function(data){
$scope.drawOrder = data[0]; // will be null
$scope.hunterOrder = data[1];
});