SOQL and Apex - Taking SOQL out of the for loop - salesforce

I have 2 objects, say object 1 and object 2.
I first iterate over a collection of object 1 records.
Id Object2Id = 'Some Id';
for (Object1 obj1 : objectOneCollection ) {
String query = 'SELECT Id FROM Object2 WHERE Id =:' + Object2Id + ' AND ' + obj1.SOQLClause__c;
List<Object2> list = Database.query(query);
//do some processing with list
}
Hope my code says it all. I know having this Database.query() inside the above for loop is a bad practice. In other words, having SOQL inside for loops is something we are not supposed to do.
Can you help me to find a way to take this Database.query() out of the for loop making sure it still matches my requirement ?
Because, there is a field in Object1 containing a string to be matched in the where clause of object2.

Related

Get list of parent records from multilevel SOQL query

I'm trying to combine 3 separate SOQL queries into one, but still end up with 3 separate lists for ease of use and readability later.
List<Object__c> objectList = [SELECT Name, Id, Parent_Object__r.Name, Parent_Object__r.Id,
(SELECT Name, Id FROM Child_Objects__r)
FROM Object__c];
I know I can get a list of child objects thus:
List<Child_Object__c> childObjectList = new List<Child_Object__c>();
for(Object__c object : objectList){
childObjectList.addAll(object.Child_Objects__r);
}
How would I go about adding the Parent_Object__c records to their own list?
I'm assuming a map could be used to deal with duplicates, but how do I get this Parent_Object__c data into that map?
You are basically there.
All lookup fields are available in your example as object.Parent_Object__r. Use a Set to natively avoid duplicates. No deduping required on your part!
Set<Parent_Object__c> parentObjectSet = new Set<Parent_Object__c>();
List<Child_Object__c> childObjectList = new List<Child_Object__c>();
for(Object__c object : objectList){
childObjectList.addAll(object.Child_Objects__r);
parentObjectSet.add(object.Parent_Object__r);
}
Edit:
As per #eyescream (trust him!) you are indeed better off with a map to avoid duplicates.
So the above code would just be slightly different:
Map<Id, Parent_Object__c> parentObjectMap = new Map<Id, Parent_Object__c>();
List<Child_Object__c> childObjectList = new List<Child_Object__c>();
for(Object__c object : objectList){
childObjectList.addAll(object.Child_Objects__r);
if (object.Parent_Object__r != null) {
parentObjectMap.put(object.Parent_Object__r.Id, object.Parent_Object__r);
}
}

How to retrieve child object data from dynamic Sooql Query resultset?

I need to fetch the name of objects from custom setting and based on the name I need to create a dynamic Sooql for getting child objects id in order to share them with specific users.
Please see below part of trigger code that I am trying in order to achieve this.
SubQ =',(select id from ' + CustomMap.get(objName).API_Field__c + ')';
String queryStr=' select id '+ SubQuery + ' from Account where id in:accId';
List<Account> objdata =Database.query(queryStr);
for(String objName : CustomMap.keyset()) {
// Here custom map contains object name as key.
// objdata is the Account List created from sooql.
for(Account ac : objdata){
for(objName obj : ac.objName ){
if(recordIdsMap.containsKey('ObjName')){
List<String> recordIds = recordIdsMap.get('ObjName');
recordIds.add(obj.Id);
system.debug('recordIds' + recordIds);
recordIdsMap.put('ObjName',recordIds);
}
else{
system.debug('Recordid' + obj.id);
recordIdsMap.put('ObjName', new List<String> {obj.id});
}
but its giving typecasting errors here at line:
for(objName obj : ac.objName ){
because objname is string and not object here and even if I try to convert it to sobject it won't work because soject id supermost object and won't be able to call it from account.
If I hardcode the object here like so:
for(cases obj : ac.cases){
it will work but my requirement is to make it generic.

Dapper Results(Dapper Row) with Bracket Notation

According to the Dapper documentation, you can get a dynamic list back from dapper using below code :
var rows = connection.Query("select 1 A, 2 B union all select 3, 4");
((int)rows[0].A)
.IsEqualTo(1);
((int)rows[0].B)
.IsEqualTo(2);
((int)rows[1].A)
.IsEqualTo(3);
((int)rows[1].B)
.IsEqualTo(4);
What is however the use of dynamic if you have to know the field names and datatypes of the fields.
If I have :
var result = Db.Query("Select * from Data.Tables");
I want to be able to do the following :
Get a list of the field names and data types returned.
Iterate over it using the field names and get back data in the following ways :
result.Fields
["Id", "Description"]
result[0].values
[1, "This is the description"]
This would allow me to get
result[0].["Id"].Value
which will give results 1 and be of type e.g. Int 32
result[0].["Id"].Type --- what datattype is the value returned
result[0].["Description"]
which will give results "This is the description" and will be of type string.
I see there is a results[0].table which has a dapperrow object with an array of the fieldnames and there is also a result.values which is an object[2] with the values in it, but it can not be accessed. If I add a watch to the drilled down column name, I can get the id. The automatically created watch is :
(new System.Collections.Generic.Mscorlib_CollectionDebugView<Dapper.SqlMapper.DapperRow>(result as System.Collections.Generic.List<Dapper.SqlMapper.DapperRow>)).Items[0].table.FieldNames[0] "Id" string
So I should be able to get result[0].Items[0].table.FieldNames[0] and get "Id" back.
You can cast each row to an IDictionary<string, object>, which should provide access to the names and the values. We don't explicitly track the types currently - we simply don't have a need to. If that isn't enough, consider using the dapper method that returns an IDataReader - this will provide access to the raw data, while still allowing convenient call / parameterization syntax.
For example:
var rows = ...
foreach(IDictionary<string, object> row in rows) {
Console.WriteLine("row:");
foreach(var pair in row) {
Console.WriteLine(" {0} = {1}", pair.Key, pair.Value);
}
}

Lua - How to check if list contains element

I need some help with my lua script for a game. I need to check if my inventory in the game contains any id from a list.
Here's a piece of my list:
local Game_Items = {
{id = 7436, name = "angelic axe", value = 5000},
{id = 3567, name = "blue robe", value = 10000},
{id = 3418, name = "bonelord shield", value = 1200},
{id = 3079, name = "boots of haste", value = 30000},
{id = 7412, name = "butcher's axe", value = 18000},
{id = 3381, name = "crown armor", value = 12000}
}
The following code might look a bit weird since you don't know what it's for, but it's basically this: the list above is a list of items in my game, and inside the game theres an inventory where you can keep items and stuff. Now I want to check if my inventory contains any of those IDs.
I tried adding 2 of the id's manually and it worked, but my list of items contains over 500 items in total and I don't want to write them all out. Is there a way to put the whole list and check if it's in there somehow?
if not table.contains({ 3035, 3043, Game_Items[id] }, tempItemCounter.id) then
This is what I tried so far. Those two first id's work 3035 and 3043, then I tried all my whole list and only check the Ids. but I dont know how to do that. That code does not work. Could anyone just help me include the whole list of id's in the table.contains ?
Basically wanna include my whole list in that line, without typing out all IDs manually.
Shouldn't Game_Items[id] work? Doesn't that mean all the "id" inside "Game_Items"?
Thanks!
No it doesn't mean that. If foo is a table, then foo[id] looks for a field in foo that is called whatever id refers to, such as a string (so if id is 1 you will get foo[1], if id is "bar" you will get foo.bar, etc).
You can't do it in one line, but you can create a function that will allow you to write your if condition. I'm not sure what tempItemCounter is but assuming that your inventory is a map of keys to entries of the form
inventory = {
[1234] = {....},
[1235] = {....},
...
}
where each integer key is unique, and assuming you want true only if all items are in inventory, then you could do this:
function isAllInInventory(items, inventory)
for i,item in ipairs(items) do
if inventory[item.id] == nil
return false
end
end
return true
end
if isAllInInventory(Game_Items, inventory) then
...
end

How to populate and then sort or randomize an array?

I am using ColdFusion 9.1. I am coding using CFSCRIPT.
I am creating a quiz. Query one will go get an image of a person and the person of the person in the image. This will be the correct answer. Query two will go get two other people who are not in the image. I want to put this into an array and then sort the array so that the correct answer isn't always at the top or the bottom or in the middle.
Here is my pseudo SQL:
QUERY ONE NAME = GET CORRECT ANSWER
SELECT TOP 1 ImageID, PersonID, FirstName, LastName
FROM IMAGES
QUERY TWO NAME = GET TWO INCORRECT ANSWERS
SELECT TOP 2 PersonID, FirstName, LastName
FROM IMAGES
WHERE ImageID IS NOT THE CORRECT ANSWER
I "think" I want my array to look like this:
PersonID="1234";
FirstName="Bob";
LastName="Jones";
I need to loop through each query and populate this array with the three people returned from the query. Like this, but this code doesn't work:
<cfscript>
PersonArray = arrayNew(1);
for (i = 1; i lte GetTwoWrong.RecordCount; i++) {
Person = structNew();
Person.PersonID = GetTwoWrong.PersonID[i];
Person.FirstName = GetTwoWrong.FirstName[i];
Person.LastName = GetTwoWrong.LastName [i];
PersonArray = arrayAppend(PersonArray , Person);
}
</cfscript>
Then I need to sort the array by FirstName, LastName, or PersonID to create any kind of randomness.
<cfscript>
PersonArray = arraySort(PersonArray numeric);
</cfscript>
Then I will need to output the answers. The answer will be clickable. On click, I will send the PersonID via jQuery to check the answer for correctness (and store the selection).
So, how do I create an array and then populate it from two different queries and then sort it? Should I be using an array at all? A structure?
I would do this in the database, which will be far less complicated. Here's an example:
SELECT * FROM (
SELECT TOP 1 1 AS Correct, ImageID, PersonID, FirstName, LastName, UniqueToCorrectAnswer
FROM IMAGES
UNION ALL
SELECT TOP 2 0 AS Correct, ImageID, PersonID, FirstName, LastName, NULL AS UniqueToCorrectAnswer
FROM IMAGES
WHERE ImageID IS NOT THE CORRECT ANSWER
) AllAnswers
ORDER BY NewID()
The two queries on either side of the UNION can be totally different against different databases, tables, and criteria, so long as they both return the same columns. You'll notice in the second query, I'm returning a NULL value for something that doesn't exist there, but does exist in the first one (just an example). So you can see how you can return different values from each side of the UNION and get them in a completely random order.
This is something that database servers are much better at than application code. If you need to (somewhere on the page) deal with your correct and incorrect answers differently, you can then use a Query of Query to pull just the correct or incorrect answers out of the base query.
Just in case you really want to sort and use a random array, as usual, Ben Nadel has done all the work for us:
http://www.bennadel.com/blog/280-Randomly-Sort-A-ColdFusion-Array-Updated-Thanks-Mark-Mandel.htm
You then need to change your original code to use a Duplicate function in your ArrayAppend to prevent reference issues. So change this:
PersonArray = arrayAppend(PersonArray , Person);
To this:
arrayAppend(PersonArray , Duplicate(Person));
ArrayAppend also returns a true or false value, so your assignment of the result back to the array is wiping out the information already in there. I always confuse that and ListAppend, which does indeed return the modified list.
PersonArray = arrayNew(1);
Person = structNew();
Person.PersonID = GetOneRight.PersonID;
Person.FirstName = GetOneRight.FirstName;
Person.LastName = GetOneRight.LastName;
ArrayAppend(PersonArray , Duplicate(Person));
for (i = 1; i lte GetTwoWrong.RecordCount; i++) {
Person = structNew();
Person.PersonID = GetTwoWrong.PersonID[i];
Person.FirstName = GetTwoWrong.FirstName[i];
Person.LastName = GetTwoWrong.LastName[i];
ArrayAppend(PersonArray , Duplicate(Person));
}
//Now shuffle it using Ben's code
CreateObject("java", "java.util.Collections").Shuffle(PersonArray);

Resources