As mentioned in the comments, Apex is largely case-insensitive. There are some places where case sensitivity matters (things stored in maps or sets, String.contains()
, anything involving a hash), but most of the time case does not matter.
So on the line String S = String.valueOf(I);
, your loop variable i
is shadowing your Integer I
inside the loop. S
ends up always being '0'
as a result. Since you're re-setting the loop variable to 0
each time, I'd expect this to loop infinitely (or rather, until Salesforce stops execution for your code going over the CPU governor limit).
If you'd get past that issue, I suspect that your failure to reset your m
variable would cause issues as well.
Apex is based on some version of Java, so (almost) all code must be in a method inside of a class. Anonymous apex allows you to run code without explicitly putting it into a method or class, but it's still a generally good idea to follow the language conventions (especially when you're learning).
Giving variables meaningful names (numberOfDigits, givenInteger, runningSum as opposed to l, i, m) will help make your code easier to read. Perhaps that isn't so important here, but it's important in general.
With all of that in mind, here's how I would write this
public class HappyNumber{
// From https://mathworld.wolfram.com/HappyNumber.html
// and http://oeis.org/A039943
// we know that if we reach any of these numbers (other than 1, which
// I've excluded) then the number is not 'happy'
// The 'new set<type>{ value }' syntax allows us to initialize the collection
// with literal values.
// 'new set<type>()', the typical constructor (with parenthesis) does not
// allow us to do that (but if you wanted to copy a set into a new set you
// could do Set<type> otherSet = new Set<type>(existingSet); )
private Set<Integer> nonHappyIntermediates = new Set<Integer>{
0, 4, 16, 20, 37, 42, 58, 89, 145
};
public Boolean isHappy(Integer givenInteger){
// The strict definition of happy numbers requires the input to be
// positive.
// It's generally accepted practice to perform your checks early.
// If you're going to guard against null values, the null check must
// always be the first check.
// Comparisons on integers are null-safe, so the null check here isn't
// necessary but I'm keeping it to illustrate the point (check null first)
if(givenInteger == null || givenInteger < 0){
// Throwing an exception might be better, but I don't feel like
// going that far
return false;
}
// I think this is one of the rare times where using a while loop
// instead of a for loop makes sense
// We stop looping if givenInteger is ever one of the non-happy
// intermediates (note the negation, '!' at the start of the statement.)
// Set.contains() is a fast operation
while(!nonHappyIntermediates.contains(givenInteger)){
String intAsString = String.valueOf(givenInteger);
Integer stringLength = intAsString.length();
// It's important to reset the running sum on each iteration
// The easiest way to force yourself to do that is to define
// this variable inside the scope of the loop
Integer runningSum = 0;
for(Integer index = 0; index < stringLength; index++){
// substring() would work too, but I think mid() is a little
// easier to read and a little harder to mess up
Integer digit = Integer.valueOf(intAsString.mid(index, 1));
runningSum += digit * digit;
}
if(runningSum == 1){
return true;
}
// Primitive types (like Integer, String, Boolean, etc...) are
// passed into functions by value.
// So overwriting it like this (in the method) is safe
givenInteger = runningSum;
}
return false;
}
}
HappyNumber decider = new HappyNumber();
Integer testInt = 19;
// The 'boolean test ? <value if true> : <value if false>' is a ternary
// It can act as a shortened version of an if/else (both the true and false results
// must be the same type)
system.debug(testInt + (decider.isHappy(testInt) ? ' is ' : ' is not ') + 'a happy Number');
// Doing string concatenation manually can be messy/error prone.
// An alternative is to use String.format() with placeholders
system.debug(String.format('{0} {1} a happy number', new List<Object>{
testInt, // gets substituted in place of {0}
decider.isHappy(testInt) ? 'is' : 'is not' // gets substituted in place of {1}
});