Better solution than else if with ranged data - java

I have a simple java method that returns colors based on the HSB value converted from an RGB. It works (needs some tweaking), but I use a series of else if and nested if statements to return the data I want. I had heard that HashMaps and String Factories were better, but I couldn't see how these worked with ranged data. Is there a better solution that works with ranged data like this?
Snippet:
public static String getColorName() {
getHSB(rgb);
if(hsbH >= 45 && hsbH < 75) {
if(hsbS > 0 && hsbS < 45 && hsbB > 70){
return "White/Off White";
} else if(hsbS > 0 && hsbS < 45 && hsbB < 10) {
return "Dark Yellow";
} else {
return "Yellow";
}
} else if(hsbH >= 15 && hsbH < 45) {
if(hsbS > 0 && hsbS < 45 && hsbB > 70){
return "White/Off White";
} else if(hsbS > 0 && hsbS < 45 && hsbB < 10) {
return "Dark Orange";
} else {
return "Orange";
}
...

If you have a single range dimension, you can use a TreeMap with floorEntry() or ceilingEntry(). But for multiple range dimensions I don't really see how to make this happen.
Instead, what I would do is specify some kind of rule Object:
public class Rule{
private int maxH = Integer.MAX_VALUE;
private int maxS = Integer.MAX_VALUE;
private int maxB = Integer.MAX_VALUE;
private int minH = Integer.MIN_VALUE;
private int minS = Integer.MIN_VALUE;
private int minB = Integer.MIN_VALUE;
public Rule maxH(int maxH){this.maxH=maxH;return this;}
public Rule minH(int minH){this.minH=minH;return this;}
public Rule maxS(int maxS){this.maxS=maxS;return this;}
public Rule minS(int minS){this.minS=minS;return this;}
public Rule maxB(int maxB){this.maxB=maxB;return this;}
public Rule minB(int minB){this.minB=minB;return this;}
public boolean appliesTo(HSB hsb){
return minH < hsb.getH() && hsb.getH() < maxH &&
minB < hsb.getB() && hsb.getB() < maxB &&
minS < hsb.getS() && hsb.getS() < maxS ;
}
}
Construct them like this:
Rule rule = new Rule().maxB(123).minH(45).maxH(122);
And keep them in a map together with the Strings (you'll probably want to implements equals() / hashCode() first).
Now iterate over the map's entrySet(), and when a rule applies, you have your color name.

Creating an HSB class could definitely make the code more readable. Below I'm using the ceilingEntry() method of TreeMap which could be argued is less readable than a multitude of if statements with explicit minimums and maximums. However, it has the added benefit of not leaving any holes. (i.e., if somebody sets up ranges of 0-5, 6-10, etc., the if statements need to include a <= or => as part of the comparison or there will be a gap.)
public class HueSatBright {
int hue,sat, brightness;
static TreeMap<Integer,String> colorMap = new TreeMap<Integer,String>();
static {
colorMap.put(15,"Red");
colorMap.put(45,"Orange");
colorMap.put(75,"Yellow");
}
HueSatBright(int hue, int sat, int brightness) {
this.hue = hue;
this.sat = sat;
this.brightness = brightness;
}
public String toString() {
return (isKindaWhite()) ? "White/Off White" : getModifier() + getBaseColor();
}
public boolean isKindaWhite() {
return (sat > 0 && sat < 45 && brightness > 70);
}
public String getModifier() {
return (sat < 10) ? "Dark " : "";
}
public String getBaseColor() {
return colorMap.ceilingEntry(hue).getValue();
}
}

Look carefully, there is a lot of repetition and very obvious structure in your code! Here is what I came up with, as far as I remember most of the job was done using automatic refactorings in my favourite IDE:
public static String getColorName() {
getHSB(rgb);
if (hsbH < 15)
return colorName(hsbB, hsbS, "Red");
if (hsbH < 45)
return colorName(hsbB, hsbS, "Orange");
if (hsbH < 75)
return colorName(hsbB, hsbS, "Yellow");
//...
}
private static String colorName(int hsbB, int hsbS, String color) {
final boolean smallSaturation = hsbS > 0 && hsbS < 45;
if (smallSaturation) {
if (hsbB > 70)
return "White/Off White";
if (hsbB < 10)
return "Dark " + color;
}
return color;
}
If you use Sean Patrick Floyd's advice of using TreeMap this code will be even simpler (I could help myself):
public static String getColorName(final int hsbH, final int hsbB, final int hsbS) {
NavigableMap<Integer, String> colorRanges = new TreeMap<Integer, String>();
colorRanges.put(0, "Red");
colorRanges.put(15, "Orange");
colorRanges.put(75, "Yellow");
//...
return colorName(hsbB, hsbS, colorRanges.floorEntry(hsbH).getValue());
}
Note that colorRanges ranges should be defined once and reused.
Risking being downvoted here is a nice way you can write this literally using Scala and simple DSL:
implicit def toIntBetween(x: Int) = new {
def between(left: Int) = new {
def and(right: Int) = {
x >= left && x < right
}
}
}
def getColorName = {
if(hsbH between 45 and 75) {
//...
}
}
Fancy if(hsbH between 45 and 75) construct actually translates to:
if(toIntBetween(hsbH).between(45).and(75))

Related

How to simplify the code if there are many logical operators "or - ||"

How to simplify the code if there are many logical operators "||"
if (Boolean.TRUE.equals(fileBeforeCompressTmp1Passport > 10485760
|| fileAfterCompressionTmp1Passport < 10240
|| fileBeforeCompressTmp1Passport < fileAfterCompressionTmp1Passport
|| fileBeforeCompressTmp1Medical > 10485760
|| fileAfterCompressionTmp1Medical < 10240
|| fileBeforeCompressTmp1Medical < fileAfterCompressionTmp1Medical
|| value.phone().toString().length() != 10
|| validationRegExp.onlyNumbersRegExp(value.phone().toString())
|| serviceJpa.existsLogisticsPersonByPhone(value.phone())
|| value.email().length() < 8
|| value.email().length() > 58
|| validationRegExp.emailValidationRegExp(value.email())
|| value.password().length() < 6
|| value.password().length() > 24
|| validationRegExp.passwordValidationRegExp(value.password())
|| value.surname().length() < 1
|| value.surname().length() > 45
|| validationRegExp.onlyLettersCyrillic(value.surname())
|| value.name().length() < 1
|| value.name().length() > 45
|| validationRegExp.onlyLettersCyrillic(value.name())
|| value.middleName().length() < 1
|| value.middleName().length() > 45
|| validationRegExp.onlyLettersCyrillic(value.middleName())
|| value.dateBirth().toString().length() != 10
|| validationRegExp.validationDateRegExp(formattedString)
|| value.numberPassport().toString().length() != 10
|| validationRegExp.onlyNumbersRegExp(value.numberPassport().toString())
|| serviceJpa.existsLogisticsPersonByNumberPassport(value.numberPassport())
|| value.region().length() != 7
|| validationRegExp.onlyLettersCyrillic(value.region())
|| value.city().length() < 2
|| value.city().length() > 25
|| validationRegExp.onlyLettersCyrillic(value.city())
) {
Files.deleteIfExists(ofPassport);
return ResponseEntity.ok(new MessageResponse(HttpStatus.OK.value(), STATIC_OK));
}
Below I show two methods, they are "emailValidationRegExp" and "passwordValidationRegExp". These methods validate the email address and password using a regular expression. "Bohemian" asked me to show them, so I show these methods.
private static final Pattern patternEmail = Pattern.compile("^[\\w.-]*#[\\w-]*+.+\\w$");
private static final Pattern patternPassword = Pattern.compile("^[0-9a-zA-Z##$]+$");
public boolean emailValidationRegExp(String email) {
Matcher matcherEmail = patternEmail.matcher(email);
return !matcherEmail.matches();
}
public boolean passwordValidationRegExp(String password) {
Matcher matcherPassword = patternPassword.matcher(password);
return !matcherPassword.matches();
}
A solution to validate data using the validation framework
Since we do not have the entire code here, I'll assume you are actually performing various validations before taking an action on data.
I assume that the data to validate comes from a remote client.
But it could also work between two services I think.
Perhaps it will help.
I see that you use ResponseEntity.ok(), so I could assume you work with SpringFramework.
The solution I propose will actually return an error (that you can turn into HTTP Status) if the conditions are not met, and will let program continue if the conditions are met (HTTP 200 / Ok).
If you have all the variables stored inside object fields, I'll call that a storage class here, you could possibly use the Java validation framework.
Once you have the validation framework in your dependencies (eg. spring-boot-starter-validation, for SpringBoot), you annotate the storage object parameter with #Valid annotation, to tell SpringFramework you want to run a validation here. It will then look for special validation annotations inside your object.
Inside your storage class (MyPayload), you can add annotations defining validation rules for each field.
For example, min value, max value, Regular Expression pattern,etc.
//Method requesting the validation of one argument
ResponseEntity<MessageResponse> doSomeAction(#Valid #RequestBody MyPayload payload) {
...
return ResponseEntity.ok(new MessageResponse(HttpStatus.OK.value(), STATIC_OK));
}
//Storage structure to validate
#Getter // LOMBOK annotations, to generate getters and setters
#Setter
public class MyPayload
{
#Size(min = 3, max = 15)
private String email;
#Min(10485760)
private long fileBeforeCompressTmp1Passport;
}
As you can see, the code will be more readable and you'll get rid of the big boolean comparison.
If the conditions are not met, an exception will be thrown.
You can possibly handle the exception with an ExceptionHandler.
See more info here:
https://www.baeldung.com/spring-boot-bean-validation
For anything that does not fit in that Validation Framework, you can still perform validation with dedicated methods, as suggested in other answers.
Seeing the fact I do not have the code around, I imagine this solution may not fit exactly your needs, but perhaps this will give you food for thoughts and you could a variation based on this. You could for instance have a method calling a Service validation method with a try/catch.
If the solution logic does not work with ||, you can think it as a &&-logic and invert the Min/Max for instance.
Extra note: If you can't avoid copying a structure to another to fit this solution in your code, have a look at the library Mapstruct.
Best practice would likely be to break your '||/or' statements into their own boolean sub-methods, grouped by related checks. I.E. grouping all the email checks in a method.
boolean methodName()
{
if (email.length() < 8){
return true
} //do this for each if, return false at the bottom.
}
If one of them returns true, the method returns true. Do this for each field, and then you can '||' them together in the main call and it'll look a bit cleaner. Depending on what you're trying to accomplish with your code, this may not be the most efficient, but it'll look cleaner.
You could divide these into separate methods and have them return a boolean. Say you have a method like:
public boolean checkFileBeforeCompressTmp1Medical()
{
//perform check
}
Then you can have an overarching method that runs all these check methods and assigns their results to a variable and check the result of the variable after the checks have ran:
public void runChecks()
{
boolean checkResult = False;
checkResult = checkFileBeforeCompressTmp1Medical();
if (checkResult)
{
//do stuff
}
}
In general, you just have to split this checks with groupt and move each of it to the separate mehtod with apropriate name. It simplify your code.
Then you can create a separate class like ValueValidator and use it to encapsulate all checks there.
public class ValueValidator {
private final ValidationRegExp validationRegExp = new ValidationRegExp();
private final ServiceJpa serviceJpa = new ServiceJpa();
private final String formattedString = "";
private final Predicate<Value> isPhoneValid = value -> {
Phone phone = value.phone();
String str = phone.toString();
return str.length() == 10 && validationRegExp.onlyNumbersRegExp(str)
&& !serviceJpa.existsLogisticsPersonByPhone(phone);
};
private final Predicate<Value> isEmailValid = value -> isInRange(value.email(), 8, 58)
&& validationRegExp.emailValidationRegExp(value.email());
private final Predicate<Value> isPasswordValid = value -> isInRange(value.password(), 6, 24)
&& validationRegExp.passwordValidationRegExp(value.password());
private final Predicate<Value> isDateBirthValid = value -> {
Date dateBirth = value.dateBirth();
return dateBirth.toString().length() == 10
&& validationRegExp.validationDateRegExp(formattedString);
};
private final Predicate<Value> isNumberPasswordValid = value -> {
NumberPassword numberPassword = value.numberPassport();
String str = numberPassword.toString();
return str.length() == 10
&& validationRegExp.onlyNumbersRegExp(str)
&& !serviceJpa.existsLogisticsPersonByNumberPassport(numberPassword);
};
private final Predicate<Value> isRegionValid = value -> value.region().length() == 7
&& validationRegExp.onlyLettersCyrillic(value.region());
private final Predicate<Value> isSurnameValid = value -> isValid(value.surname(), 1, 45);
private final Predicate<Value> isNameValid = value -> isValid(value.name(), 1, 45);
private final Predicate<Value> isMiddleNameValid = value -> isValid(value.middleName(), 1, 45);
private final Predicate<Value> isCityValid = value -> isValid(value.city(), 2, 25);
private final Predicate<Value> isValueValid = isPhoneValid.and(isEmailValid)
.and(isPasswordValid)
.and(isSurnameValid)
.and(isNameValid)
.and(isMiddleNameValid)
.and(isDateBirthValid)
.and(isNumberPasswordValid)
.and(isRegionValid)
.and(isCityValid);
public boolean isValueValid(Value value) {
return isValueValid.test(value);
}
public boolean isRangeValid(int before, int after) {
return before <= 10485760 && after >= 10240 && before >= after;
}
private boolean isValid(String name, int lo, int hi) {
return isInRange(name, lo, hi) && validationRegExp.onlyLettersCyrillic(name);
}
private static boolean isInRange(String str, int lo, int hi) {
return str.length() >= lo && str.length() <= hi;
}
}
In this case your code could be reduced to:
Value value = new Value();
ValueValidator valueValidator = new ValueValidator();
if (!valueValidator.isValueValid(value)
|| !valueValidator.isRangeValid(fileBeforeCompressTmp1Passport, fileAfterCompressionTmp1Passport)
|| !valueValidator.isRangeValid(fileBeforeCompressTmp1Medical, fileAfterCompressionTmp1Medical)) {
Files.deleteIfExists(ofPassport);
return ResponseEntity.ok(new MessageResponse(HttpStatus.OK.value(), STATIC_OK));
}
If possible I think you should dlegate the validation of the value object to its own class.
I'd move the validations inside the class of value, I assume it is Passport, so something like this
class Passport {
private String phone;
private String email;
private String password;
private String surname;
private String name;
private String middleName;
private String dateBirth;
private String numberPassport;
private String region;
private String city;
public boolean isPhoneValid() {
return phone.toString().length() != 10
|| validationRegExp.onlyNumbersRegExp(phone.toString())
|| serviceJpa.existsLogisticsPersonByPhone(phone);
}
public boolean isEmailValid() {
// ....
}
// .... Other isValidXxxx()
public boolean isValid() {
return isPhoneValid()
|| isEmailValid()
|| isPasswordValid()
|| isSurnameValid()
|| isNameValid()
|| isMiddleNameValid()
|| isDateBirthValid()
|| isNumberPassportValid()
|| isRegionValid()
|| isCityValid();
}
}
Then have the compression validation in your class, in a separate method, and do
public static void main(String[] args) {
if (validateCompression() || value.isValid()) {
Files.deleteIfExists(ofPassport);
return ResponseEntity.ok(new MessageResponse(HttpStatus.OK.value(), STATIC_OK));
}
}
private static boolean validateCompression(
int fileBeforeCompressTmp1Passport,
int fileAfterCompressionTmp1Passport,
int fileBeforeCompressTmp1Medical,
int fileAfterCompressionTmp1Medical) {
return fileBeforeCompressTmp1Passport > 10485760
|| fileAfterCompressionTmp1Passport < 10240
|| fileBeforeCompressTmp1Passport < fileAfterCompressionTmp1Passport
|| fileBeforeCompressTmp1Medical > 10485760
|| fileAfterCompressionTmp1Medical < 10240
|| fileBeforeCompressTmp1Medical < fileAfterCompressionTmp1Medical;
}

How to get rid of multiple if statements in java?

Is there a better way to write this constructor which has multiple if statements and multiple arguments? I'm a noob to programming so any leads would be helpful.
public Latency(final double full, final double cpuOne, final double cpuTwo, final double cpuThree, final double cpuFour) {
if (full > 10.0 || (full <= 0.0)) {
throw new IllegalArgumentException("Must check the values");
}
this.full = full;
if (cpuOne == 0 && cpuTwo == 0 && cpuThree == 0 && cpuFour == 0) {
throw new IllegalArgumentException("not all can be zero");
} else {
if (cpuOne == 0.5) {
this.cpuOne = full;
} else {
this.cpuOne = cpuOne;
}
if (cpuTwo == 0.5) {
this.cpuTwo = full;
} else {
this.cpuTwo = cpuTwo;
}
if (cpuThree == 0.5) {
this.cpuThree = full;
} else {
this.cpuThree = cpuThree;
}
if (cpuFour == 0.5) {
this.cpuFour = full;
} else {
this.cpuFour = cpuFour;
}
}
}
I think this code doesn't need much of context as it is pretty straight forward.
I found out that we can't use switch statements for type double. How to optimize this?
There are a number of possible ways of refactoring the code that you've written, and there are pros and cons of each one. Here are some ideas.
Idea One - use the conditional operator
You could replace the else block with code that looks like this. This is just effectively a shorter way of writing each of the inner if/else blocks. Many people find this kind of form more readable than a bunch of verbose if/else blocks, but it takes some time to get used to it.
this.cpuOne = cpuOne == 0.5 ? full : cpuOne;
this.cpuTwo = cpuTwo == 0.5 ? full : cpuTwo;
this.cpuThree = cpuThree == 0.5 ? full : cpuThree;
this.cpuFour = cpuFour == 0.5 ? full : cpuFour;
Idea Two - move common functionality to its own method
You could have a method something like this
private static double changeHalfToFull(double value, double full) {
if (value == 0.5) {
return full;
} else {
return value;
}
}
then call it within your constructor, something like this.
this.cpuOne = changeHalfToFull(cpuOne);
this.cpuTwo = changeHalfToFull(cpuTwo);
this.cpuThree = changeHalfToFull(cpuThree);
this.cpuFour = changeHalfToFull(cpuFour);
This has the advantage that the key logic is expressed only once, so it's less error prone than repeating code over and over.
Idea Three - use arrays
You could use an array of four elements in the field that stores these values. You could also use an array for the constructor parameter. This has a huge advantage - it indicates that the four CPU values are somehow all "the same". In other words, there's nothing special about cpuOne compared to cpuTwo, for example. That kind of messaging within your code has real value to someone trying to understand this.
public Latency(final double full, final double[] cpuValues) {
// validation conditions go here ...
this.cpuValues = new double[4];
for (int index = 0; index <= 3; index++) {
if (cpuValues[index] == 0.5) {
this.cpuValues[index] = full;
} else {
this.cpuValues[index] = cpuValues[index];
}
}
}
Or a combination
You could use some combination of all these ideas. For example, you might have something like this, which combines all three of the above ideas.
public Latency(final double full, final double[] cpuValues) {
// validation conditions go here ...
this.cpuValues = new double[4];
for (int index = 0; index <= 3; index++) {
this.cpuValues[index] = changeHalfToFull(cpuValues[index]);
}
}
private static double changeHalfToFull(double value, double full) {
return value == 0.5 ? full : value;
}
There are obviously other possibilities. There is no single correct answer to this question. You need to choose what you're comfortable with, and what makes sense in the larger context of your project.
DRY - Don't Repeat Yourself
Each if is essentially the same. Put it in a separate method and call the method once for each cpu* variable.
public class Latency {
private double full;
private double cpuOne;
private double cpuTwo;
private double cpuThree;
private double cpuFour;
public Latency(final double full,
final double cpuOne,
final double cpuTwo,
final double cpuThree,
final double cpuFour) {
if (full > 10.0 || (full <= 0.0)) {
throw new IllegalArgumentException("Must check the values");
}
this.full = full;
if (cpuOne == 0 && cpuTwo == 0 && cpuThree == 0 && cpuFour == 0) {
throw new IllegalArgumentException("not all can be zero");
}
else {
this.cpuOne = initCpu(cpuOne);
this.cpuTwo = initCpu(cpuTwo);
this.cpuThree = initCpu(cpuThree);
this.cpuFour = initCpu(cpuFour);
}
}
private double initCpu(double cpu) {
return cpu == 0.5 ? full : cpu;
}
public static void main(String[] arg) {
new Latency(9.99, 8.0, 7.0, 6.0, 0.5);
}
}

How do I declare the identifier in this smart contract in remix?

Need to identify totalSupply.
If you find any potential errors or something that can cause an issue, I would highly appreciate you for helping me, preventing any errors in the future.
To avoid adding parameters, because I don't know how to put them into the contract when verification happens
How can I add in the name, symbol in the contract I believe those will be strings and the maxNftSupply as 10000 and the saleStart time those would be uints.
If I can have a verbal conversation with someone that can help, I would love that opportunity.
contract TestMB is ERC721, Ownable {
using SafeMath for uint256;
string public TEST_PROVENANCE = "";
uint256 public startingIndexBlock;
uint256 public startingIndex;
uint256 public constant testPrice = 80000000000000000; //0.08 ETH
uint public constant maxTestPurchase = 20;
uint256 public MAX_TEST;
bool public saleIsActive = false;
uint256 public REVEAL_TIMESTAMP;
constructor(string memory name, string memory symbol, uint256 maxNftSupply, uint256 saleStart) ERC721(name, symbol) {
MAX_TEST = maxNftSupply;
REVEAL_TIMESTAMP = saleStart + (86400 * 9);
}
function withdraw() public onlyOwner {
(bool os, ) = payable(owner()).call{value: address(this).balance}('');
require(os);
}
function reserveTest() public onlyOwner {
uint supply = totalSupply();
uint i;
for (i = 0; i < 30; i++) {
_safeMint(msg.sender, supply + i);
}
}
function setRevealTimestamp(uint256 revealTimeStamp) public onlyOwner {
REVEAL_TIMESTAMP = revealTimeStamp;
}
/*
* Set provenance once it's calculated
*/
function setProvenanceHash(string memory provenanceHash) public onlyOwner {
TEST_PROVENANCE = provenanceHash;
}
function setBaseURI(string memory _setBaseURI) public onlyOwner {
setBaseURI = _setBaseURI;
}
function flipSaleState() public onlyOwner {
saleIsActive = !saleIsActive;
}
function mintTest(uint numberOfTokens) public payable {
require(saleIsActive, "Sale must be active to mint Test");
require(numberOfTokens <= maxTestPurchase, "Can only mint 20 tokens at a time");
require(totalSupply().add(numberOfTokens) <= MAX_TEST, "Purchase would exceed max supply of Test");
require(testPrice.mul(numberOfTokens) <= msg.value, "Ether value sent is not correct");
for(uint i = 0; i < numberOfTokens; i++) {
uint mintIndex = totalSupply();
if (totalSupply() < MAX_TEST) {
_safeMint(msg.sender, mintIndex);
}
}
if (startingIndexBlock == 0 && (totalSupply() == MAX_TEST || block.timestamp >= REVEAL_TIMESTAMP)) {
startingIndexBlock = block.number;
}
}
function setStartingIndex() public {
require(startingIndex == 0, "Starting index is already set");
require(startingIndexBlock != 0, "Starting index block must be set");
startingIndex = uint(blockhash(startingIndexBlock)) % MAX_TEST;
if (block.number.sub(startingIndexBlock) > 255) {
startingIndex = uint(blockhash(block.number - 1)) % MAX_TEST;
}
if (startingIndex == 0) {
startingIndex = startingIndex.add(1);
}
}
function emergencySetStartingIndexBlock() public onlyOwner {
require(startingIndex == 0, "Starting index is already set");
startingIndexBlock = block.number;
}
}
Your contract inherits from ERC721 instead of ERC721Enumerable, which contains the totalSupply() function, while ERC721 does not. What you can do is import ERC721Enumerable into the contract and have it inherit like so:
contract TestMB is ERC721, ERC721Enumerable, Ownable {}

why are all my array index values set to the same value

This portion of my program seems to be giving me a problem:
public static double[] getBonusAmt(boolean[] bonusEligibility, int[] numYrsFlown, double[] bonusAmt) {
bonusAmt = new double[bonusEligibility.length];
double bonus = 0;
for (boolean b : bonusEligibility) {
for (int i : numYrsFlown) {
if (i >= 9 && b == true) {
bonus = 2410.00;
}
else if (i < 9 && i >= 6 && b == true) {
bonus = 1206.00;
}
else if (i < 6 && i >= 2 && b == true) {
bonus = 515.00;
}
else if (i < 2 && b == true) {
bonus = 0.00;
}
}
}
return bonusAmt;
}
Input/Output:
Name: [joe, james]
Years flown: [2, 2]
Miles flown: [45, 43]
Average miles between pilots: 44
Bonus eligibility: [true, false]
Bonus amount: [0.00, 0.00]
Joe should be earning a bonus because his miles flown is greater than the average, but his amount is zero. The expected bonus amount for Joe should be 515.00 because one, he is eligible for a bonus and two, has only flown for 2 years.
Can anyone see why the bonus amount is always zero even if I enter another person that has flown more than the average?
Your method assigns values to the bonus variable but returns a bonusAmt variable, which is never assigned, so its values remain 0.0.
Your nested loops don't make much sense. It looks like you need a single regular for loop, assuming that the i'th index of bonusEligibility array corresponds with the i'th index of the numYrsFlown array.
public static double[] getBonusAmt(boolean[] bonusEligibility, int[] numYrsFlown) {
double[] bonusAmt = new double[bonusEligibility.length];
for (int i = 0; i < bonusEligibility.length; i++) {
if (numYrsFlown[i] >= 9 && bonusEligibility[i]) {
bonus = 2410.00;
}
else if (numYrsFlown[i] < 9 && numYrsFlown[i] >= 6 && bonusEligibility[i]) {
bonusAmt[i] = 1206.00;
}
else if (numYrsFlown[i] < 6 && numYrsFlown[i] >= 2 && bonusEligibility[i]) {
bonusAmt[i] = 515.00;
}
else if (numYrsFlown[i] < 2 && bonusEligibility[i]) {
bonusAmt[i] = 0.00;
}
}
return bonusAmt;
}
BTW, there's no point in passing the bonusAmt array as an argument to the method, since the method assigns to it a reference to a new array.
You forgot to set bonusAmt to the selected bonus value.
Here is a more object oriented way to do what you want. No need to accept this answer as Eran's solution explains your error perfectly ... This is just another way of doing it ...
public class MainApp {
public static void main(String[] args) {
AirMilesCustomer[] customers = new AirMilesCustomer[] {
new AirMilesCustomer("John", true, 2),
new AirMilesCustomer("Jane", true, 5),
new AirMilesCustomer("Sally", true, 7),
new AirMilesCustomer("Bill", false, 10),
new AirMilesCustomer("Stacy", true, 15)
};
for(AirMilesCustomer customer : customers) {
System.out.println(customer);
}
}
}
class AirMilesCustomer {
private String _name;
private boolean _bonusEligibility;
private int _numYrsFlown;
public AirMilesCustomer(String name, boolean bonusEligibility, int numYrsFlown) {
_name = name;
_bonusEligibility = bonusEligibility;
_numYrsFlown = numYrsFlown;
}
public String getName() {
return _name;
}
public boolean isBonusEligibility() {
return _bonusEligibility;
}
public int getNumYrsFlown() {
return _numYrsFlown;
}
public double getBonusAmount() {
double bonus = 0.00;
if (_numYrsFlown >= 9 && _bonusEligibility) {
bonus = 2410.00;
}
else if (_numYrsFlown < 9 && _numYrsFlown >= 6 && _bonusEligibility) {
bonus = 1206.00;
}
else if (_numYrsFlown < 6 && _numYrsFlown >= 2 && _bonusEligibility) {
bonus = 515.00;
}
else if (_numYrsFlown < 2 && _bonusEligibility) {
bonus = 0.00;
}
return bonus;
}
public String toString() {
return "[" + _name + "][" + _numYrsFlown + "][" + _bonusEligibility + "][" + getBonusAmount() + "]";
}
}

Detecting if a character in a String is an emoticon (using Android)

Like the title says. I want to find out if a given java String contains an emoticon.
I can't use Character.UnicodeBlock.of(char) == Character.UnicodeBlock.EMOTICONS since that requires API level 19.
I found this code for iOS but it isn't really applicable since it looks like java and objective-c handle surrogate pairs in different manners.
The documentations I've looked through tell me that:
A char value, therefore, represents Basic Multilingual Plane (BMP) code points, including the surrogate code points, or code units of the UTF-16 encoding
I'm not quite sure what that means. Does that simply mean that they also include the BMP point as their first number?
According to Wikipedia the emoticon set lies between 0x1f600 and 0x1f64f but I don't know how to check if the char is in that range.
I had hoped that something like this would work but it didn't
if (0x1f600 <= a && a <= 0x1f64f)
{
Print.d("Unicode", "groovy!");
}
So how do I go about this?
Four years later...
At this time, it might make more sense to take advantage of EmojiCompat. This code presumes you initialized EmojiCompat when your app was starting up. The basic idea here is to have EmojiCompat process your CharSequence, inserting instances of EmojiSpan wherever any emoji appear, and then examine the results.
public static boolean containsEmoji(CharSequence charSequence) {
boolean result = false;
CharSequence processed = EmojiCompat.get().process(charSequence, 0, charSequence.length() -1, Integer.MAX_VALUE, EmojiCompat.REPLACE_STRATEGY_ALL);
if (processed instanceof Spannable) {
Spannable spannable = (Spannable) processed;
result = spannable.getSpans(0, spannable.length() - 1, EmojiSpan.class).length > 0;
}
return result;
}
If you want to collect a list of the unique emoji that appear within a given CharSequence, you could do something like this, iterating over the results of getSpans() and finding the start and end of each span to capture the emoji discovered by EmojiCompat:
#NonNull
public static List<String> getUniqueEmoji(CharSequence charSequence) {
Set<String> emojiList = new HashSet<>();
CharSequence processed = EmojiCompat.get().process(charSequence, 0, charSequence.length() -1, Integer.MAX_VALUE, EmojiCompat.REPLACE_STRATEGY_ALL);
if (processed instanceof Spannable) {
Spannable spannable = (Spannable) processed;
EmojiSpan[] emojiSpans = spannable.getSpans(0, spannable.length() - 1, EmojiSpan.class);
for (EmojiSpan emojiSpan : emojiSpans) {
int spanStart = spannable.getSpanStart(emojiSpan);
int spanEnd = spannable.getSpanEnd(emojiSpan);
CharSequence emojiCharSequence = spannable.subSequence(spanStart, spanEnd);
emojiList.add(String.valueOf(emojiCharSequence));
}
}
return emojiList.size() > 0 ? new ArrayList<>(emojiList) : new ArrayList<String>();
}
UPDATE: Here's an example of EmojiCompat initialization. This static method can be called from your Application onCreate() method, passing in the Application itself as the Context param.
#JvmStatic
fun initEmojiCompat(context: Context) {
if (emojiCompatConfig != null) {
// alternatively, EmojiCompat.reset() could be called here
logger().w(LOGTAG, "EmojiCompat already initialized.")
return
}
// "Noto Color Emoji Compat" doesn't have graphics for the following emojis:
// U+1F5E3 "speaking head" (required)
// U+1F441 "eye" (required)
// U+1F575 "detective" (nice to have)
val fontRequest = FontRequest(
"com.google.android.gms.fonts",
"com.google.android.gms",
"Noto Color Emoji Compat",
R.array.com_google_android_gms_fonts_certs
)
emojiCompatConfig = FontRequestEmojiCompatConfig(context, fontRequest)
.setReplaceAll(false)
.setEmojiSpanIndicatorEnabled(false)
.registerInitCallback(initCallback)
.also {
EmojiCompat.init(it)
}
}
I was in fact able to use the linked iOS code to create the following function. I didn't realize that a String that contains, for example, a single emoticon will have a length of 2. So you can check if a character is in fact a surrogate.
I'm not entirely sure how to handle else if (substring.length > 1) from the iOS code but I think Character.isHighSurrogate(myChar) does the same job in that instance.
private boolean containsIllegalCharacters(String displayName)
{
final int nameLength = displayName.length();
for (int i = 0; i < nameLength; i++)
{
final char hs = displayName.charAt(i);
if (0xd800 <= hs && hs <= 0xdbff)
{
final char ls = displayName.charAt(i + 1);
final int uc = ((hs - 0xd800) * 0x400) + (ls - 0xdc00) + 0x10000;
if (0x1d000 <= uc && uc <= 0x1f77f)
{
return true;
}
}
else if (Character.isHighSurrogate(hs))
{
final char ls = displayName.charAt(i + 1);
if (ls == 0x20e3)
{
return true;
}
}
else
{
// non surrogate
if (0x2100 <= hs && hs <= 0x27ff)
{
return true;
}
else if (0x2B05 <= hs && hs <= 0x2b07)
{
return true;
}
else if (0x2934 <= hs && hs <= 0x2935)
{
return true;
}
else if (0x3297 <= hs && hs <= 0x3299)
{
return true;
}
else if (hs == 0xa9 || hs == 0xae || hs == 0x303d || hs == 0x3030 || hs == 0x2b55 || hs == 0x2b1c || hs == 0x2b1b || hs == 0x2b50)
{
return true;
}
}
}
return false;
}
This is how Telegram does it:
private static boolean isEmoji(String message){
return message.matches("(?:[\uD83C\uDF00-\uD83D\uDDFF]|[\uD83E\uDD00-\uD83E\uDDFF]|" +
"[\uD83D\uDE00-\uD83D\uDE4F]|[\uD83D\uDE80-\uD83D\uDEFF]|" +
"[\u2600-\u26FF]\uFE0F?|[\u2700-\u27BF]\uFE0F?|\u24C2\uFE0F?|" +
"[\uD83C\uDDE6-\uD83C\uDDFF]{1,2}|" +
"[\uD83C\uDD70\uD83C\uDD71\uD83C\uDD7E\uD83C\uDD7F\uD83C\uDD8E\uD83C\uDD91-\uD83C\uDD9A]\uFE0F?|" +
"[\u0023\u002A\u0030-\u0039]\uFE0F?\u20E3|[\u2194-\u2199\u21A9-\u21AA]\uFE0F?|[\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55]\uFE0F?|" +
"[\u2934\u2935]\uFE0F?|[\u3030\u303D]\uFE0F?|[\u3297\u3299]\uFE0F?|" +
"[\uD83C\uDE01\uD83C\uDE02\uD83C\uDE1A\uD83C\uDE2F\uD83C\uDE32-\uD83C\uDE3A\uD83C\uDE50\uD83C\uDE51]\uFE0F?|" +
"[\u203C\u2049]\uFE0F?|[\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE]\uFE0F?|" +
"[\u00A9\u00AE]\uFE0F?|[\u2122\u2139]\uFE0F?|\uD83C\uDC04\uFE0F?|\uD83C\uDCCF\uFE0F?|" +
"[\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA]\uFE0F?)+");
}
It is Line 21,026.
Try this...
if (Integer.parseInt("1f600", 16) <= (int)'☺' && (int)'☺' <= Integer.parseInt("1f64f", 16)) {
Print.d("Unicode", "groovy!");
}
This might work because the hexidecimal value and the char value are both being converted to ints.
Here's some Kotlin that relies on java.lang.Character api (granted the original poster can't use this). I have found it pretty reliably tells apart an emoji from 'special characters' and non-latin alphabets etc. Try it.
import java.lang.Character.*
import java.lang.Character.UnicodeBlock.MISCELLANEOUS_TECHNICAL
import java.lang.Character.UnicodeBlock.MISCELLANEOUS_SYMBOLS
import java.lang.Character.UnicodeBlock.VARIATION_SELECTORS
fun isStringEmoji(someString: String): Boolean {
if (someString.isNotEmpty() && someString.length < 5) {
val firstCodePoint = codePointAt(someString, 0)
val lastCodePoint = codePointBefore(someString, someString.length)
if (isValidCodePoint(firstCodePoint) && isValidCodePoint(lastCodePoint)) {
if (isSupplementaryCodePoint(firstCodePoint) ||
isSupplementaryCodePoint(lastCodePoint) ||
UnicodeBlock.of(firstCodePoint) == MISCELLANEOUS_SYMBOLS ||
UnicodeBlock.of(firstCodePoint) == MISCELLANEOUS_TECHNICAL ||
UnicodeBlock.of(lastCodePoint) == VARIATION_SELECTORS
) {
return true
}
}
}
return false
}

Categories