Bulkify Lead industry recategorization - salesforce

I am trying to setup a job that I can run couple times a day to keep our leads categorized to meet our sales and Marketing approved list. My tests scripts work great. However, when I go to run it in my sandbox I am running with a bunch of dummy data I am running into issues with DML limits.
I am looking for ideas how I can process as many leads as possible efficiently.
Edit:
18:00:20.106 (10106660336)|EXCEPTION_THROWN|[377]|System.LimitException: Too many DML statements: 151
18:00:20.106 (10106789908)|FATAL_ERROR|System.LimitException: Too many DML statements: 151
Number of DML statements: 151 out of 150 ******* CLOSE TO LIMIT
Number of DML rows: 150 out of 10000
global class Industry_Mappings Implements Schedulable {
Public List < Lead > DisplayIndLeads;
global void execute(SchedulableContext sc) {
DisplayIndLeads = new List < Lead > ();
DisplayIndLeads = [select Industry, Sub_Industry__c from Lead where Sub_Industry__c = null and Industry < > Null and IsConverted < > True];
//This will create a little efficenty with the for loops
Integer skip = 0;
Integer i = 0;
//Advertising and Marketing
List < string > AdvertisingAndMarketing = new List < string > {
'Design', 'Graphic Design', 'Market Research'
};
//List for looping
List < lead > leadstoupdate = new List < Lead > {};
//This starts the Loop for the leads
for (Lead ld : DisplayIndLeads) {
//lead l = (Lead)ld;
//leadstoupdate.size();
//Advertising and Marketing
if (skip == 0) {
for (string AnM : AdvertisingAndMarketing) {
if (ld.Industry == AnM) {
ld.Sub_Industry__c = ld.Industry;
ld.Industry = 'Advertising and Marketing';
skip = 99;
leadstoupdate.add(ld);
}
}
}
System.debug('***** What is in leadstoupdate: ' + leadstoupdate);
update leadstoupdate;
}
}
}

You're using DML operation inside the for loop what leads to the error.
You just need to mode the DML statement outside the for loop
for (Lead ld : DisplayIndLeads) {
if (skip == 0) {
for (string AnM : AdvertisingAndMarketing) {
if (ld.Industry == AnM) {
ld.Sub_Industry__c = ld.Industry;
ld.Industry = 'Advertising and Marketing';
skip = 99;
leadstoupdate.add(ld);
}
}
}
}
update leadstoupdate;
- - - - UPDATE - - - -
The reason why your code updated just one lead is the fact that you have a condition if (skip == 0) which false (because you set it to 99) after adding first Lead to leadsToUpdate list.
Change this condition or even remove it because I do not see any reasons for that.
for (Lead ld : DisplayIndLeads) {
for (string AnM : AdvertisingAndMarketing) {
if (ld.Industry == AnM) {
ld.Sub_Industry__c = ld.Industry;
ld.Industry = 'Advertising and Marketing';
leadstoupdate.add(ld);
}
}
}
update leadstoupdate;

Related

Faster way to iterate between 3 arrays who match values and meet conditions? typescript

So i'm having trouble trying to build out an excel sheet faster than 20 seconds. I'm using the below to compare 3 different arrays to then print out the answer i need.
for (let i = 0; i < this.arrayNumberOne[0].length; i++) {
let orangeOne = this.arrayNumberOne[0][i]['item_1'];
let orangeTwo = this.arrayNumberOne[0][i]['item_2'];
let orangeThree = orangeOne.concat(orangeTwo)
for (let k = 0; k < this.arrayNumberTwo[0].length; k++) {
let appleOne = this.arrayNumberTwo[0][k]['item_1'];
for (let j = 0; j < this.arrayNumberThree[0].length; j++) {
let grapeOne = this.arrayNumberThree[0][j]['item_1'];
let grapeTwo = this.arrayNumberThree[0][j]['item_2'];
let grapeThree = this.arrayNumberThree[0][j]['item_3'];
if (orangeThree == grapeOne && appleOne == grapeTwo) {
if (grapeThree == null || grapeThree == '') {
// print to response to excel
}
else if (grapeThree == 'sells') {
// print stuff to excel
}
else if (grapeThree == 'buys') {
// print stuff to excel
}
}
}
}
}
I was looking at hashmaps and interfaces, but im not quite sure how i could apply that here. I would appreciate any alternatives to making the above faster.
Edit:
Playground Link
What sets off a red flag here is that you have 3 nested loops, but your data is 2D, so you would expect at most 2 nested loops (one for X and one for Y). What you want to focus on is that the process for getting the value of each cell should be as fast as possible, since that is what needs to happens to largest number of times.
The key here is pre-process your values (what goes in the cells of your spreadsheet, buy/sell/blank) in a way that makes it very quick look those up.
For example:
// Index all actions by player id and item name
const indexedActions: {
[playerId: string]: {
[itemCatName: string]: string
}
} = {}
// Loop through all actions once to index them.
for (const {playerId, itemCatName, action} of actions) {
if (!indexedActions[playerId]) indexedActions[playerId] = {}
indexedActions[playerId][itemCatName] = action
}
After that runs you'll have data like:
{
"12": {
"potionsmall-potion": 'buy'
// etc...
}
// etc...
}
That's important because now looking up any cell is as easy as:
indexedActions[playerId][itemName]
Which lets you completely remove that most inner loop.
// Loop through items
for (let i = 0; i < items.length; i++) {
let itemCat = items[i]['itemCategory']
let itemNam = items[i]['itemName']
let combineCat = itemCat.concat(itemNam)
// Loop through players
for (let k = 0; k < players.length; k++) {
let playerIdentify = players[k]['playerId']
// Lookup what this player did with this item
const actionToDo = indexedActions[playerIdentify][combineCat]
// do stuff with action
}
}
Before Optimization
for every item
for every player
for every action
doLogic()
Here "doLogic" is executed itemCount * playerCount * actionCount times.
After Optimization
for every action
index each action
for every item
for every player
doLogic()
Now we do a little more work up front, but doLogic is only executed itemCount * playerCount times, which is a huge improvement.
And as a bonus it's less code and easier to read!
See playground

Importing from second array in Swift

I have 2 arrays, 1 is a master (bookArray) and the other an update list (stockBooksArray). I use the loop below to import new data into waster array using a unique record (isbn) that exists in both arrays.
Is there a way to improve the performance of the loop below?
The update array can contain a different count to the master, sometimes more, sometimes less.
for i in 0...stockBooksArray.count {
let StockFiltered = stockBooksArray.filter{$0.isbn == bookArray[i].isbn}
if StockFiltered.count != 0 {
bookArray[i].stockAmount = StockFiltered[0].Stock
bookArray[i].unitCost = StockFiltered[0].Cost
bookArray[i].dues = StockFiltered[0].dues
bookArray[i].stockRRP = StockFiltered[0].RRP
}
}
Thanks,
Anthony
Yes there is. Right now you are looping through the bookArray array once for every stockBooksArray object. That's O(N*M) which is pretty bad.
Instead, if you can sort both arrays by ISBN, then you can step through both at the same time, visiting each element only once which would be O(N). Considerably faster, even if you include the time to sort them.
I threw the code below together pretty quick. It assumes that both arrays are sorted by isbn. I think you will find this considerably faster.
var j = 0
for book in stockBooksArray {
while bookArray[j].isbn < book.isbn && j < bookArray.count {
++j;
}
if j == bookArray.count {
break
}
else if bookArray[j].isbn == book.isbn {
bookArray[j].stockAmount = StockFiltered[0].Stock
bookArray[j].unitCost = StockFiltered[0].Cost
bookArray[j].dues = StockFiltered[0].dues
bookArray[j].stockRRP = StockFiltered[0].RRP
}
}
If you are just adding new ones you could get an NSMutableSet of the ISBNs for each, perform a set minusSet: operation removing the bookArray ISBNs from stockBooksArray and then just add the resulting ISBNs to bookArray.
Untested concept code, convert to Swift as needed:
NSMutableSet *stockBooksSet = NSMutableSet setWithArray:[stockBooksSet valueForKey:#"isbn"];
NSSet *booksSet = NSSet setWithArray:[bookArray valueForKey:#"isbn"];
[stockBooksSet minusSet: booksSet];
for (NSString *isbn in stockBooksSet) {
// add book with ism to booksSet
}
Updated code using Daniel T. method.
bookArray.sort { (lhs, rhs) in return lhs.isbn < rhs.isbn }
stockBooksArray.sort { (lhs, rhs) in return lhs.isbn < rhs.isbn }
var j = 0
for book in stockBooksArray {
while bookArray[j].isbn < book.isbn && j < bookArray.count {
++j
}
if j == bookArray.count {
break
}
else if bookArray[j].isbn == book.isbn {
bookArray[j].stockAmount = book.Stock
bookArray[j].unitCost = book.Cost
bookArray[j].dues = book.dues
bookArray[j].stockRRP = book.RRP
}
}
From 5-6 seconds on iPad2 to almost instant.
Many thanks,
Anthony

Unity3D the best way to loop through multiple dimension arrays

I am developing a game that has a winning combination array:
var allwinning = [
['000','010','020'],
['000','100','200'],
['000','001','002'],
['000','101','202'],
['000','011','022'],
['000','110','220']];
The player will need to pick more than 3 numbers randomly. If all numbers are within any of the combinations in allwinning, the player wins.
For example, if the player picks '111','110','000','220', the player will win because allwinning[5] has the combination['000','110','220'].
My question is, what is the best way to do this winning loop? I cannot figure out the optimum way to do this.
Currently, I have a playerpick array to keep what player had picked and possiblewin array:
var playerpick = new Array(['111','110','000','220']);
var playerpicksingle = playerpick[0];
var possiblewin = new Array([]);
Then I go through a loop to capture out the possible win combination first:
for(var i=0 ; i < allwinning.length - 1 ; i++)
{
for(var j=0 ; j <3 ; j++)
{
if(allwinning[i][j]==playerpicksingle)
{
possiblewin.Push(allwinning[i]);
}
}
}
Then I am stuck at this point. I really don't know what else to do.
I can think of two ways. One requires you to change your data structure and the other doesn't.
Without changes:
Sort the user input:
pickedNumbers.sort();
and start comparing. By sorting the values beforehand you know when you can back out and continue with the next set of numbers, i.e. you can back out early and don't have to compare all the values (in the average case).
function wins(picked, winning) {
var winningSet = [];
for (var i = 0; i < winning.length && winningSet.length < 3; i++) {
var set = winning[i];
winningSet = [];
var j = 0;
var k = 0;
while (j < set.length && k < picked.length && winningSet.length < 3) {
if (picked[k] === set[j]) {
winningSet.push(set[j]);
j++; // advance to next element in winning set
} else if (picked[k] > set[j]) {
// continue with the next set
break;
}
// maybe the next element in players picks will match
k++;
}
}
return winningSet.length === 3 ? winningSet : false;
}
The worst case scenario of this solution is O(n*m*l), but since the input is sorted, the average case will be better.
DEMO
With Array#some and Array#every the code becomes much more concise, though it looses the advantage of using sorted input. If your arrays are small it won't make a difference though:
function wins(picked, winning) {
return winning.some(function(set) {
return set.every(function(val) {
return picked.indexOf(val) !== -1;
});
});
}
It also won't give you the actual numbers that matched. The runtime is the same.
The second way would be to build some kind of trie instead of using an array of arrays:
var allwinning = {
'000': {
'010': {
'020': true
},
'100': {
'200': true
},
// ...
}
};
The structure should also be sorted, i.e. the keys of a level are all smaller then the keys of its sublevel etc.
Sort the user input as well and iterate over it. Whenever you found a matching key, you go one level deeper until you have three matches:
function wins(picked, winning) {
var winningSet = [];
for (var i = 0; i < picked.length && winningSet.length < 3; i++) {
if (picked[i] in winning) {
winningSet.push(picked[i]);
winning = winning[picked[i]];
}
}
return winningSet.length === 3 ? winningSet : false;
}
This solution has the worst case scenario of O(n), where n is the number of values the user picked (not taking into account the time it takes to test whether an object contains a specific property name. Often this is assumed to constant).
DEMO

How to compare 2 arrays?

I have two arrays, namely combo and truecombo. The user fills the combo with MovieClips by clicking on various buttons on the stage, truecombo is the correct combination.
At any given point (enterFrame) Flash is checking whether the two are the same, if yes, then do some stuff. For the time being this is my code (altered several times, like with Typecasting the indices, adding .parent at the end of combo[o] etc. 2 things will happen, either one or the other.
Either the statement will not be satisfied, at which point the adding and chopping of the combo array will continue, or the condition will be instantly met when combo.length = 6. Check my code.
UPDATE: I have a dropbox file with my current code. Click this for FLA link and here is the SWF link stripped down as always for ease and security.
/*stage.*/addEventListener(Event.ENTER_FRAME, checkthis);
function checkthis(e:Event)
{
for(var o:int=0;o<= combo.length; o++)
{
if((combo[o] == truecombo[o]) && (combo.length==truecombo.length))
{
equal=true;
}
}
if (equal==true)
{
stage.removeEventListener(Event.ENTER_FRAME, checkthis);
endSeq();
}
}
function endSeq():void
{
bravo.play();
for (var i:int = 0; i < combo.length; i++)
{
var element:DisplayObject = combo[i];
element.parent.removeChild(element);
}
firebb.gotoAndPlay(2);
windbb.gotoAndPlay(2);
spiritbb.gotoAndPlay(2);
earthbb.gotoAndPlay(2);
}
This is how I push my new elements to the combo array.
function add(element:DisplayObject)
{
twist.gotoAndPlay(2);
element.width = WIDTH;
element.height = HEIGHT;
if (this.combo.length >= MAX_ELEMENTS)
{
removeChild(this.combo.shift());
}
this.combo.push(element as DisplayObject);
this.addChild(element);
this.reorder();
}
function reorder()
{
for (var i:int = 0; i < combo.length; i++)
{
var element:DisplayObject = combo[i];
element.x = OFFSET_X + (i * SEP_X);
element.y = OFFSET_Y;
}
}
And this is how I have my truecombo and its contents created.
var fireb:firebtn = new firebtn();
var spiritb:spiritbtn = new spiritbtn();
var earthb:earthbtn = new earthbtn();
var windb:windbtn = new windbtn();
var combo:Array=new Array();
const truecombo:Array = [fireb,windb,spiritb,windb,earthb,fireb];
Sorry for the lack of comments, I'd guess it's pretty self-explanatory. Thanks in advance.
I believe combo[o] & truecombo[o] are two instances of the same class & you want them to be matched. If that is the case you may consider :
getQualifiedClassName(combo[o]) == getQualifiedClassName(truecombo[o])
To match the way you did, you must ensure the objects lying inside truecombo be referring to the same ones on stage & not new instances.
EDIT:
It seems you do not break the loop when the match is a success. Use this instead :
function checkthis(e:Event)
{
for(var o:int=0;o<= combo.length; o++)
if((combo[o] == truecombo[o]) && (combo.length==truecombo.length)) {
equal=true;
break;
}
if (equal) {
stage.removeEventListener(Event.ENTER_FRAME, checkthis);
endSeq();
}
}
Here's a really simple loop:
var equal:Boolean=true
if(combo.length == truecombo.length) {
for(var i:int=0; i<combo.length; i++) {
if(combo[i] != truecombo[i]) {
equal=false;
break;
}
}
} else {
equal=false;
}
if(equal) {
//do whatever
}
This assumes both are equal, until we find out otherwise. So if the lengths are different, they are not equal. If the ith element is different, they are not equal.
In the end, you check if your flag equal is true and do whatever you want to do.

jCombobox giving incremental value error on jTable

I am having problems with my code below, the code below shows a jComboBox being populated, when i select an item from this list it is added to the jTable below it.
There is alos code to check for duplicate entries ont he table. If a duplicate entry is found it should increase the qty column by one and not create a seperate entry.
This is where the problem comes in, when I press the back button on this screen and go to a different screen and then come back via same route as the first time, I get an incrementally different qty added to the table row/cell.
I have also included the code that populates the Round Details depending on Round Drop selected from table, for reference, but Im fairly certain the problem lies in the below code. The navigation is as follows...
To get to the below screen... Round Drop panel table of round drops) >> click on table row and taken to associated round details panel >> pressing the Till button takes user to screen with code below...
Test results:
First pass through below code using navigation above gives results as expected
Second pass gives an initial value of 2 (instead of one), and duplicate row increases qty by 2 instead of one
Third pass gives an initial value of 3 (instead of one), and duplicate row increases qty by 3 instead of one
Fourth pass gives an initial value of 4 (instead of one), and duplicate row increases qty by 4 instead of one
...and so on.
Any help, guidance on solution or a better design would be hugely appreciated.
Thanks
/*************Code sample ********************************/
public void tillOperations(String sourceCall) {
final DefaultTableModel model = (DefaultTableModel)main.tillPanel.tblTillSale.getModel();
if (main.tillPanel.cmbTillProdSelect.getItemCount() < 1) {
for (int d = 0; d < roundStockObj.length ; d++) {
main.tillPanel.cmbTillProdSelect.addItem(roundStockObj[d].getDescription());
}}
main.tillPanel.tblTillSale.removeRowSelectionInterval(0, model.getRowCount() - 1);
main.tillPanel.cmbTillProdSelect.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent f)
{
int qty = 1;
for (int index = 0; index < 4; index++) {
addSelectedItem[index] = "";
}
int row;
selectedItem = null;
main.tillPanel.tblTillSale.removeRowSelectionInterval(0, model.getRowCount() - 1);
selectedItem = main.tillPanel.cmbTillProdSelect.getSelectedItem();
for (int d = 0; d < roundStockObj.length; d++) {
if (selectedItem.equals(roundStockObj[d].getDescription())) {
addSelectedItem[0] = roundStockObj[d].getDescription();
addSelectedItem[1] = Integer.toString(qty);
addSelectedItem[2] = Double.toString(roundStockObj[d].getPrice()).trim();
addSelectedItem[3] = Double.toString(roundStockObj[d].getPrice()).trim();
//break;
}
}
if(model.getRowCount() == 0) { //check if model is empty
model.addRow(new String[]{addSelectedItem[0], addSelectedItem[1], addSelectedItem[2], addSelectedItem[3]});
}
else { //check if there is a duplicate row
int duplicateRow = -1;
for (row = 0 ; row < model.getRowCount(); row++) {
if(addSelectedItem[0].equals(main.tillPanel.tblTillSale.getModel().getValueAt(row,0))) {
duplicateRow = row;
break;
}
}
if(duplicateRow == -1) { //if there is no duplicate row, append
model.addRow(new String[]{addSelectedItem[0], addSelectedItem[1], addSelectedItem[2], addSelectedItem[3]});
}
else { //if there is a duplicate row, update
main.tillPanel.jLabel1.setText(addSelectedItem[1]);
DecimalFormat fmtObj = new DecimalFormat("####0.00");
int currentValue = Integer.parseInt(main.tillPanel.tblTillSale.getValueAt(row, 1).toString().trim());
int newValue = currentValue + 1;
Integer newValueInt = new Integer(newValue);
model.setValueAt(newValueInt, row, 1);
double unitPrice = Double.parseDouble(main.tillPanel.tblTillSale.getValueAt(row, 2).toString().trim());
double newPrice = newValue * unitPrice;
Double newPriceDbl = new Double(newPrice);
main.tillPanel.tblTillSale.setValueAt(fmtObj.format(newPriceDbl), row, 3);
}
}
main.tillPanel.tblTillSale.removeRowSelectionInterval(0, model.getRowCount() - 1);
for (int index = 0; index < 4; index++) {
addSelectedItem[index] = "";
}
}
});
//This code loads the specific Round Details, based on the selection form the round drops table
public void displayRoundDropDetails() {
DefaultTableModel model = (DefaultTableModel)main.selectRoundDropPanel.tblSelectRoundDrop.getModel();
if (!loaded) {
for (int d = 0; d < roundDropsData.length; d++) {
if (roundDropsData[d][0].equals(defaultRoundID)) {
model.addRow(new Object[]{roundDropsData[d][3], roundDropsData[d][2],
roundDropsData[d][4], roundDropsData[d][5]});
}
}
loaded = true;
}
main.selectRoundDropPanel.tblSelectRoundDrop.addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent evt)
{
int row = 0;
row = main.selectRoundDropPanel.tblSelectRoundDrop.getSelectedRow();
for (int index = 0; index < roundDropsData.length; index++) {
if (roundDropsData[index][3].equals(
main.selectRoundDropPanel.tblSelectRoundDrop.getModel().getValueAt(row, 0))) {
main.roundDetailsPanel.txtRoundDetailsAddress.setText(roundDropsData[index][6] + "\n"
+ roundDropsData[index][7] + ", " + roundDropsData[index][8] + "\n" +
roundDropsData[index][9]);
main.roundDetailsPanel.lblRoundDetailsName.setText(roundDropsData[index][2]);
main.roundDetailsPanel.txtRoundDetailsInstuct.setText(roundDropsData[index][10]);
main.roundDetailsPanel.txtDropDetailsIn.setText(roundDropsData[index][4]);
main.roundDetailsPanel.txtDropDetailsOut.setText(roundDropsData[index][5]);
main.roundDetailsPanel.txtRoundDetailsInstruct.setText(roundDropsData[index][12]);
break;
}
}
Globals.CURRENT_COMPONENT = "selectRoundDropPanel";
showRoundDetailsPanel();
}
});
}
Try changing the listener for JComboBox. try using stateChangeListener.

Resources