dart - Sort a list of futures - sorting

So I have a set of options that each contain an int value representing their ordinal.
These options are stored in a remote database with each option being a record.
As such when I fetch them from the db I end up with a list of future:
e.g. List<Future<Option>>
I need to be able to sort these Options.
The following dart pad shows a simplified view of what I'm trying to achieve:
https://dartpad.dartlang.org/a5175401516dbb9242a0edec4c89fef6
The Options MUST be futures.
My original solution was to copy the Options into a list, 'complete' them, then sort the list.
This however caused other problems and as such I need to do an 'insitu' sort on the original list.

You cannot sort the futures before they have completed, and even then, you need to extract the values first.
If you need to have a list of futures afterwards, this is what I would do:
List<Future<T>> sortFutures<T>(List<Future<T>> input, [int compare(T a, T b)]) {
var completers = [for (var i = 0; i < input.length; i++) Completer<T>()];
Future.wait(input).then((values) {
values.sort(compare);
for (int i = 0; i < values.length; i++) {
completers[i].complete(values[i]);
}
});
return [for (var c in completers) c.future];
}
This does not return the original futures because you don't know the ordering at the time you have to return them. It does return futures which complete with the same value.
If any of the futures completes with an error, then this blows up. You'll need more error handling if that is possible.

Gentlfolk,
thanks for the help.
julemand101 suggestion of using Future.wait() lead me to the answer.
It also helped me better understand the problem.
I've done a new gist that more accurately shows what I was attempting to do.
Essentilly when we do a db request over the network we get an entity back.
The problem is that the entity will often have references to other entities.
This can end in a whole tree of entities needing to be returned.
Often you don't need any of these entities.
So the solution we went for is to only return the database 'id' of each child entity (only the immediate children).
We then store those id's in a class RefId (see below).
The RefId is essentially a future that has the entities id and knows how to fetch the entity from the db.
When we actually need to access a child entity we force the RefId to complete (i.e. retrieve the entity across the network boundary).
We have a whole caching scheme to keep this performant as well as the ability to force the fetching of child elements, as part of the parent request, where we know up front they will be needed.
The options in my example are essentially menu items that need to be sorted.
But of course I can't sort them until they have been retrieved.
So a re-written example and answer:
https://dartpad.dartlang.org/369e71bb173ba3c19d28f6d6fec2072a
Here is the actual IdRef class we use:
https://dartpad.dartlang.org/ba892873a94d9f6f3924436e9fcd1b42
It now has a static resolveList method to help with this type of problem.
Thanks for your assistance.

Related

MongoTemplate, Criteria and Hashmap

Good Morning.
I'm starting to learn some mongo right now.
I'm facing this problem right now, and i'm start to think if this is the best approach to resolve this "task", or if is bettert to turn around and write another way to solve this "problem".
My goal is to iterate a simple map of values (key) and vector\array (values)
My test map will be recived by a rest layer.
{
"1":["1","2","3"]
}
now after some logic, i need to use the Dao in order to look into db.
The Key will be "realm", the value inside vector are "castle".
Every Realm have some castle and every castle have some "rules".
I need to find every rules for each avaible combination of realm-castle.
AccessLevel is a pojo labeled by #Document annotation and it will have various params, such as castle and realm (both simple int)
So the idea will be to iterate a map and write a long query for every combination of key-value.
public AccessLevel searchAccessLevel(Map<String,Integer[]> request){
Query q = new Query();
Criteria c = new Criteria();
request.forEach((k,v)-> {
for (int i: Arrays.asList(v)
) {
q.addCriteria(c.andOperator(
Criteria.where("realm").is(k),
Criteria.where("castle").is(v))
);
}
});
List<AccessLevel> response=db.find(q,AccessLevel.class);
for (AccessLevel x: response
) {
System.out.println(x.toString());
}
As you can see i'm facing an error concerning $and.
Due to limitations of the org.bson.Document, you can't add a second '$and' expression specified as [...]
it seems mongo can't handle various $and, something i'm pretty used to abuse over sql
select * from a where id =1 and id=2 and id=3 and id=4
(not the best, sincei can use IN(), but sql allow me)
So, the point is: mongo can actualy work in this way and i need to dig more into the problem, or i need to do another approach, like using criterion.in(), and make N interrogation via mongotemplate one for every key in my Map?

Why does my forget() method not remove object from collection?

I have a collection of "Tickets", using the random collection utility method I select one from the list. The "Tickets" collection should now remove (or forget) that randomly selected ticket so I can further process that collection. Using the forget method doesn't appear to do what is described in the documentation or (more likely I'm missing something).
Can someone spot whats wrong in my code?
$tickets = Tickets::all();
$total_winners = 5;
$selected_tickets = $tickets->random($total_winners);
$jackpot_winner = $selected_tickets->random();
$selected_tickets->forget($jackpot_winner->id); // this line should remove the $jackpot_winner
When I print the contents of $selected_tickets on lines 3 and lines 5, they have the exact same items, including the $jackpot_winner.
Forget function uses the collection key not the id from the model. To achieve what you want you may use this method:
$selected_tickets = $selected_tickets->except($jackpot_winner->id);
https://laravel.com/docs/8.x/collections#method-except

How to get a SOQL query out of a for loop

Code added.
I have searched endlessly for a solution here and cannot find one, please help!
I have three objects (A, B, and C). A has a lookup to B, and B is the master to C (detail). Both A and C have many records related to each B record.
I want to have a job run that gets a subset of records from object C (it will usually be around 5,000 records). Then go through each of those and get the records on Object A that lookup to the same Object B record, summarize an Object A number field, and put that on the C record.
I have successfully gotten this to work in small scale, <100 Object C records. But each Object C record requires a new SOQL query since I am iterating through them in a for loop after I get all the Object C records. Plus I know this it is not best practice to ever have a query in a loop.
How can I get this to work? Since the records share the relationship with Object B, is there another way to get the data from the Object A records that match? Or is there some way to pull two lists, one Object C and one Object A. Then summarize the Object A records and line the lists up some how?
Thanks in advance!
Code:
public class nightlyJob {
public static void updateNumbers(){
integer I = 29;
List<ObjectC__c> CUpdateList = new List<ObjectC__c>();
List<ObjectC__c> CpullList =
[SELECT ID, Index__c, ObjectB__r.id
FROM ObjectC__c
WHERE Index__c = :I];
for(ObjectC__c s : CpullList){
List<ObjectA__c> AList =
[SELECT ObjectB__c, Number__c
FROM ObjectA__c
WHERE ObjectB__c = :s.ObjectB__r.Id];
decimal NumSum = 0;
for(ObjectA__c a : AList){
NumSum = a.Number__c + NumSum;
}
s.Num__c = NumSum;
CUpdateList.add(s);
}
update CUpdateList;
}
}
It looks like you are really missing several fundamental concepts at the moment.
The biggest problem you are up against in SFDC development is that "database" operations are very expensive and are strictly limited. It's not just a matter of "best practice": if in a single transaction you exceed these limits -- number of SOQL calls, number of records returned, number of records updated, number of DML statements, etc. -- your transaction will fail. For details, search online for "Salesforce Execution Governors and Limits".
You can write code that works within these limitations, but there is a bit of a learning curve.
First, learn to use collections with SOQL queries to get your SOQL queries out of loops. This is a.k.a. "bulkfication" and it fundamental to SFDC development:
List<ObjectC__c> CpullList =
[SELECT ID, Index__c, ObjectB__r.id
FROM ObjectC__c
WHERE Index__c = :I];
// Create a map with the results of this query.
// key=ObjectC__c.Id, value = Object__c record
Map<Id, ObjectC__c> objCmap = Map<Id, ObjectC__c>(CpullList);
// Build a set of all the Object_B id's from this result set
Set<Id> objBids = new Set<Id>();
for (ObjectC__c record : CpullList) {
objBids.add(record.ObjectB__r.id);
}
// Now you can use only one SOQL query instead of a loop
List<ObjectA> AList = [SELECT ObjectB__c, Number__c
FROM ObjectA__c
WHERE ObjectB__c in:objBids];
Next, use "SOQL aggregate functions" whenever you can. Example: in your code here, you could use "SUM()" and "group by" instead of performing these calculations with loops:
// Get the sum of ObjectA__c.Number__c for each Object B in objBIds
AggregateResult[] groupedResults = [select ObjectB__c,
sum(Number__c) sumA
from ObjectA__c
where ObjectB__c in: objBids
group by ObjectB__c];
for (AggregateResult ar : groupedResults) {
System.debug('Object B Id' + ar.get('Objectb__c'));
System.debug('Sum of ObjectA__c.Number__c' + ar.get('sumA'));
// Here, you might want to build a Map<Id, Integer> sumAmap:
// key=Object B ID, value=sumA
// and then use it along with objCmap to build a collection of Object C's
// for your update statement...
}
You can continue this process and apply these ideas to make the code more efficient.
But even after you have your methods working as efficiently as possible, you still may run into limits due to the number of records you're dealing with. At that point, you will need to learn about the Batchable interface, the Queuable interface and #future calls (how to process a larger number of records, split across transactions) That's really too much to information to cover in a single SO answer.

Select distinct value from a list in linq to entity

There is a table, it is a poco entity generated by entity framework.
class Log
{
int DoneByEmpId;
string DoneByEmpName
}
I am retrieving a list from the data base. I want distinct values based on donebyempid and order by those values empname.
I have tried lot of ways to do it but it is not working
var lstLogUsers = (context.Logs.GroupBy(logList => logList.DoneByEmpId).Select(item => item.First())).ToList(); // it gives error
this one get all the user.
var lstLogUsers = context.Logs.ToList().OrderBy(logList => logList.DoneByEmpName).Distinct();
Can any one suggest how to achieve this.
Can I just point out that you probably have a problem with your data model here? I would imagine you should just have DoneByEmpId here, and a separate table Employee which has EmpId and Name.
I think this is why you are needing to use Distinct/GroupBy (which doesn't really work for this scenario, as you are finding).
I'm not near a compiler, so i can't test it, but...
Use the other version of Distinct(), the one that takes an IEqualityComparer<TSource> argument, and then use OrderBy().
See here for example.

How can I use set operations to delete objects in an entitycollection that match a collection of view models?

Here is a very basic example of what I want to do. The code I have come up with seems quite verbose... ie looping through the collection, etc.
I am using a Telerik MVC grid that posts back a collection of deleted, inserted and updated ViewModels. The view models are similar but not exactly the same as the entity.
For example... I have:
Order.Lines. Lines is an entity collection (navigation property) containing OrderDetail records. In the update action of my controller using the I have a List names DeletedLines pulled from the POST data. I also have queried the database and have the Order entity including the Lines collection.
Now I basically want to tell it to delete all the OrderDetails in the Lines EntityCollection.
The way I have done it is something like:
foreach (var line in DeletedLines) {
db.DeleteObject(Order.Lines.Where(l => l.Key == line.Key).SingleOrDefault())
}
I was hoping there was a way that I could use .Interset() to get a collection of entities to delete and pass that to DeleteObject.. however, DeleteObject seems to only accept a single entity rather than a collection.
Perhaps the above is good enough.. but it seemed like there should be an easier method.
Thanks,
BOb
Are the items in DeletedLines attached to the context? If so, what about this?
foreach (var line in DeletedLines) db.DeleteObject(line);
Response to comment #1
Ok, I see now. You can make your code a bit shorter, but not much:
foreach (var line in DeletedLines) {
db.DeleteObject(Order.Lines.SingleOrDefault(l => l.Key == line.Key))
}
I'm not sure if DeleteObject will throw an exception when you pass it null. If it does, you may be even better off using Single, as long as you're sure the item is in there:
foreach (var line in DeletedLines) {
db.DeleteObject(Order.Lines.Single(l => l.Key == line.Key))
}
If you don't want to re-query the database and either already have the mapping table PK values (or can include them in the client call), you could use one of Alex James's tips for deleting without first retrieving:
http://blogs.msdn.com/b/alexj/archive/2009/03/27/tip-9-deleting-an-object-without-retrieving-it.aspx

Resources