How to create a Mixed Comparator? - java

I have a table full of values where the first 2 digits are a year the next 3 digits are a value between 0 and 999 and the final 2 characters are 2 alphaOnly characters. A few example values: 0, 0, 99001AG, 99002FG, 54001AG, 54050AB, There are also a few instances where the value is just a 6 digit String SGP4DC. There will be multiple of values that are SGP4DC. The 0's are bad data but I have to account for them for the testing purposes.
Special cases: Because of the two digit year, when sorting descending, launches from 1999 (e.g. 99001A) are always sorted as "greater" than launches from the 2000's (e.g. 06001A). The special handler should ensure that any items between 00 and 56 are sorted as greater than any items between 57 and 99.
Now my sorting goal is to first order by the first 2 digits to take care of the above special case. Then follow that up with the following 3 digits. And finally just a string sort on the last 2 characters. And finally follow that up with the String compare of values that dont start with 2 numerical digits.
Example of expected sorted in ascending order would be
0
0
60001AG
60002FB
42001AG
42002GD
APG4GP
APG4GP
Again note if the 2 leading digits are greater than or equal to 57 it represents 1957-1999. And if the 2 leading digits are less than 57 they represent 2000-2056.
Finally my code. Note I have some bogus data currently in the table with values for 0. Thus I was attempting to make them less than everything else. I do not have the power to remove the 0's so i'm trying to code around them. IE 0's will always show up after the above sorted list.
#Override
public int compare(String o1, String o2) {
if(o1.equals("0") && o2.equals("0")){
return 0;
}
System.out.println("Comparing " + o1 + " and " + o2);
if (o1.length() == 1) {
return -1;
}
if (o2.length() == 1) {
return 1;
}
String o1year = null;
String o2year = null;
Integer obj1year;
Integer obj2year;
if (o1.length() >= 2) {
o1year = o1.substring(0, 2);
}
if (o2.length() >= 2) {
o2year = o2.substring(0, 2);
}
if (isInteger(o1year)) {
if (isInteger(o2year)) {
obj1year = Integer.parseInt(o1year);
obj2year = Integer.parseInt(o2year);
// handles years 2000 - 2056 being greater than anything from
// ##57-##99
if (obj1year < 57 && obj2year > 56) {
return 1;
}
if (obj1year == obj2year) {
int returnValue = compareIncriment(o1, o2);
if(returnValue == 0){
return o1.compareToIgnoreCase(o2);
}
return returnValue;
}
if (obj1year > obj2year) {
return 1;
} else {
return -1;
}
}
return 1;
}
// object 2 starts with a 2 digit year and object 1 didnt
if (isInteger(o2year)) {
return -1;
}
// final return
return o1.compareToIgnoreCase(o2);
}
private int compareIncriment(String o1, String o2) {
// TODO Auto-generated method stub
int inc1;
int inc2;
if(isInteger(o1.substring(2, 4))){
inc1 = Integer.parseInt(o1.substring(2, 4));
}else if(isInteger(o1.substring(2, 3))){
inc1 = Integer.parseInt(o1.substring(2, 3));
}else{
inc1 = Integer.parseInt(o1.substring(2, 2));
}
if(isInteger(o2.substring(2, 4))){
inc2 = Integer.parseInt(o2.substring(2, 4));
}else if(isInteger(o2.substring(2, 3))){
inc2 = Integer.parseInt(o2.substring(2, 3));
}else{
inc2 = Integer.parseInt(o2.substring(2, 2));
}
return inc1 - inc2;
}
Updated Code***
I currently see nothing in my table and i'm getting a Comparison method violates its general contract error.

You should write unit tests for your comparator to discover bugs. You should also factor your code better, because your function is very hard to understand. First, classify the product code into the "0" case, the year-included case, and the no-year case. If the two codes are not in the same class, return the appropriate result.
If they are in the same class, factor out the specific comparisons into separate functions, or even separate Comparators. Having separate Comparators makes them easier to test; separate functions would be harder to justify making public.
I found one bug by looking at the code: for c.compare("0", "0") it returns -1 when it should return 0. Beyond that, it's really hard to tell.

Related

How would you update the values in a hashmap based on prior conditions?

Something fundamental about hashmaps and for loops is hard for me to grasp here. What I'm trying to do is add +1 to the value associated with the key based on the Keys method every time the value within the array list is associated with the key string.
So if there are 3 values in the array list that are positive then the hashmap should have the value with the key "positive" updated to 3.
Any help/advice would be appreciated - thank you.
public String Keys(double input){
if (input > 0){
System.out.println("positive");
}
else if (input < 0) {
System.out.println("negative");
}
else if (input == 0) {
System.out.println("zero");
}
return "";
}
public HashMap<String, Integer> increaseValues(ArrayList<Double> inputs){
HashMap<String, Integer> hashMap = new HashMap<>();
hashMap.put("positive", 0);
hashMap.put("negative", 0);
hashMap.put("zero", 0);
//What I tried before adding the Keys method.
//This updates the value but the loop won't continue if another input in the
//arraylist is true.
for (int i = 0; i < inputs.size(); i++){
double input = inputs.get(i);
if (input > 0){
hashMap.put("positive", 1);
} else if (input < 0){
hashMap.put("negative", 1);
} else if (input == 0){
hashMap.put("zero", 1); }
return hashMap;
}
public void main(String[] args){
ArrayList<Double> inputs = new ArrayList<>();
inputs.add(-4.56);
inputs.add(-4.66);
inputs.add(0.0);
inputs.add(6.0);
inputs.add(-6.99);
inputs.add(6.97);
}
Map.put(k, v) always overrides your previous value. You can use the "traditional approach":
if (!map.containsKey("positive"))
map.put("positive", 0);
map.put("positive", map.get("positive") + 1);
or better use the new merge funtction that is added for exactly such cases:
map.merge("positive", 1, (prev, one) -> prev + one);
But this whole logic can be greatly shortened by using Math.signum() and stream collectors:
Map<Double, Long> collect = inputs.stream()
.collect(Collectors.groupingBy(Math::signum,
Collectors.counting()));
System.out.println("positive: " + collect.get(1.0));
System.out.println("negative: " + collect.get(-1.0));
System.out.println("zero: " + collect.get(0.0));
Lets start with a quick tidy, lets create an enum and remove those nasty String constants that you don't have defined anywhere:
public enum Sign {
POSITIVE,
ZERO,
NEGATIVE;
public static Sign of(double d) {
if (d > 0) {
return POSITIVE;
}
if (d < 0) {
return NEGATIVE;
}
return ZERO;
}
}
Now we can trivially write a method to increment the appropriate value:
public void increment(final double d, final Map<Sign, Integer> count) {
count.merge(Sign.of(d), 1, Integer::sum);
}
For a quick test:
final Map<Sign, Integer> count = new EnumMap<>(Sign.class);
increment(0, count);
System.out.println(count);
increment(-1, count);
System.out.println(count);
increment(1, count);
System.out.println(count);
increment(-2, count);
System.out.println(count);
increment(2, count);
System.out.println(count);
Output:
{ZERO=1}
{NEGATIVE=1, ZERO=1}
{POSITIVE=1, NEGATIVE=1, ZERO=1}
{POSITIVE=1, NEGATIVE=2, ZERO=1}
{POSITIVE=2, NEGATIVE=2, ZERO=1}
So how does this magic work? From the documentation for Map.merge
If the specified key is not already associated with a value or is
associated with null, associates it with the given non-null value.
Otherwise, replaces the associated value with the results of the given
remapping function, or removes if the result is null. This method may
be of use when combining multiple mapped values for a key.
So it takes the key as the first argument to merge - in the case Sign.of(d); this selects the correct bucket. If the value mapped to that key is null then it simply puts a mapping for the key to the value passed as the second argument - in this case 1. Otherwise it gets a litte more complicated; it takes the value currently mapped to that key and uses the remappingFunction passed as the third argument. This is a BiFunction<V,V,V>, so it takes two arguments of type V, the value type and returns a single one - it merges the two together. Here we use Integer::sum to take the existing value, the new value and return their sum.
But we can go one step further, we can use a Stream to carry this out on an arbitrary double[]:
public Map<Sign, Long> count(final double[] d) {
return Arrays.stream(d)
.mapToObj(Sign::of)
.collect(groupingBy(identity(), () -> new EnumMap<>(Sign.class), counting()));
}
Note: I've used an EnumMap here, which is Map optimised for using an enum as the key.
You can solve it pretty concisely with streams. You need a function which turns value into the negative/zero/positive key. Then just group by this key with a counting collector. It's basically a two-liner:
List<Double> values = Arrays.asList(-4.56,-4.66,0.0, 6.0, -6.99, 6.97);
Function<Double, String> toKey = v -> v < 0 ? "negative" : (v == 0 ? "zero" : "positive");
Map<String, Long> result = values
.stream()
.collect(Collectors
.groupingBy(
toKey,
Collectors.counting()));
The method put of HashMap maps unique keys with values. It replaces the existing value with the new value in case the provided key is already present in the map.
You need to define a method similar to below.
public void addValueToList(Double d, List<Double> inputs , Map<String,Integer> map){
if( d == null ) return;
if ( isZero(d) ){
Integer count = map.get("zero");
if(count == null){
count = 1;
}else {
count ++;
}
map.put("zero",count);
}else if( isNegative(d) ) {
Integer count = map.get("negative");
if(count == null){
count = 1;
}else {
count ++;
}
map.put("negative",count);
}else {
Integer count = map.get("positive");
if(count == null){
count = 1;
}else {
count ++;
}
map.put("positive",count);
}
inputs.add(d);
}
For writing method for comparing the value you can refer https://stackoverflow.com/a/10400718/504133

Finding matching objects in Java

I'm currently trying to match 2 objects based on their values. Except, it's not a.a = a.a, but a.a = a.b and a.b = b.a. This means that overriding equals is an option but it's certainly not the right option.
While sorting these objects will make the matching time quicker, the population will be small so it is unnecessary. Also, compareTo isn't exactly right either for the same reason given for equals.
Do I simply make my own method in case? There will be 4 fields to match which is why I am not using an if statement up front.
public boolean isOpposite(Object other) {
return (this.a == other.b) ? true : false;
}
There is also the possibility that the object will implement/extend a base object to take on more fields and implement its own way of matching.
I'm considering using a LinkedList because I know it to be quicker for use than ArrayList, however I've also been considering Maps.
Edit: better explanation of objects
public class Obj {
public String a;
public String b;
public String c;
public double d;
}
The relationships are as follows:
Obj obj1, obj2;
obj1.a == obj2.b //.equals for String of course
obj1.b == obj2.a
obj1.c == obj2.c
obj1.d == obj2.d * -1
Overriding the equals or compareTo is not the right way to go, as you've mentioned. Because there is an assumption that both methods should be transitive, i.e. A eq B and B eq C => A eq C but it doesn't hold for the "opposite" objects. It's good to know, because you can't define a equivalence class and partition it into subsets, but you need to find all the pairs (depending on your use case).
Not sure, what is your goal. If you have some containers with such objects and you need to find all pairs that suffice the condition, then I am afraid you'd need to do n^2 comparisons.
I'll probably create two hash sets, one with the originals and second with the opposites and ask if the second hash set contains the opposite of each member of original hash set.
I've done some testing and determined that the cleanest way I knew how to implement this was with using ArrayList<Obj>.
This was my implementation:
public static List<ObjGroup> getNewSampleGroup(int size) {
List<ObjGroup> sampleGroup = new ArrayList<ObjGroup>();
sampleGroup.add(new ObjGroup((generateNumbers(size, 1)))); //Positives
sampleGroup.add(new ObjGroup((generateNumbers(size, -1)))); //Negatives
return sampleGroup;
}
private static List<Obj> generateNumbers(int size, int x) {
List<Obj> sampleGroup = new ArrayList<Obj>();
for (int i = 0; i < size; i ++) {
Random rand = new Random();
String randC;
String randA;
String randB;
double randD;
if (x == 1) {
randD = rand.nextInt((maxP - minP + 1) + minP);
randA = "aval";// + String.valueOf(rand.nextInt((max - min + 1) + min));
randB = "bval";// + String.valueOf(rand.nextInt((max - min + 1) + min));
randC = "cval";// + String.valueOf(rand.nextInt((max - min + 1) + min));
} else {
randD = rand.nextInt((maxP - minP + 1) + minP) * -1;
randA = "bval";// + String.valueOf(rand.nextInt((max - min + 1) + min));
randB = "aval";// + String.valueOf(rand.nextInt((max - min + 1) + min));
randC = "cval";// + String.valueOf(rand.nextInt((max - min + 1) + min));
}
sampleGroup.add(new Obj(randA, randB, randC, randD));
}
return sampleGroup;
}
public List<ObjGroup> findMatches(List<ObjGroup> unmatchedList) {
List<Obj> pivotPos = unmatchedList.get(0).getObjs(); //First grouping are positives
List<Obj> pivotNeg = unmatchedList.get(1).getObjs(); //Second grouping are negatives
List<ObjGroup> matchedList = new ArrayList<ObjGroup>();
long iterations = 0;
Collections.sort(pivotPos);
Collections.sort(pivotNeg, Collections.reverseOrder());
for (Iterator<Obj> iteratorPos = pivotPos.iterator(); iteratorPos.hasNext();) {
final Obj focus = iteratorPos.next();
iteratorPos.remove(); //Remove this once pulled as you won't match it again.
for (Iterator<Obj> iteratorNeg = pivotNeg.iterator(); iteratorNeg.hasNext();) {
final Obj candidate = iteratorNeg.next();
if (compare(focus, candidate)) {
matchedList.add(new ObjGroup(new ArrayList<Obj>() {
{
add(focus);
add(candidate);
}
}));
iteratorNeg.remove(); //Remove this once matched as you won't match it again.
break;
}
iterations ++;
}
iterations ++;
}
return matchedList;
}
I ran this against a sample size of 4,000,000 psuedo random Obj objects. This was my output:
Starting matching test.
18481512007 iterations.
3979042 matched objects.
10479 unmatched objects.
Processing time: 44 minutes.
There were 1989521 number of matches found.
Closing matching test.

Searching for a specific number in a int variable

I'm currently "learning" JavaScript + Android Studio for school and I got a little problem for which I can't find the right answer on Google:
I want to know if an int variable has a specific number, for example, I'm looking for the number 7 now int numberOne = 25824 doesn't have a 7 inside, but int numberTwo = 12387 does have one. Is there a way to search for a specific number in int variables?
I tried converting the int into a new string variable, but somehow this doesn't work :(
Here's some code I'm working with:
public int round = 1;
public String nummerSieben = "" + round;
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (round % 7 == 0 || nummerSieben.contains("7")==true) {
....
} else {
....
}
}
});
Thank you for your help!
public int round = 1;
public String nummerSieben = "" + round; // nummerSieben is now "1"
You're hard-coding the value of nummberSieben. You need presumably get some value from the view, and test that. If you get it as in int, use
Integer.toString(i).contains("7") // i is whatever number you get from your view.
If you get it as a String, then half the work is already done, and you just need
i.contains("7")
As noted above, this has nothing to do with JavaScript - both your example and my answer are in Java.
Couple of things:
Your comparison is not right, method String:contains() returns a boolean,
Module % does not assert you the number will contain 7 or one of it's multiples.
Integer.toString(value) converts easily your int to String.
Knowing this, you can do:
if (Integer.toString(round).contains("7")) {
// IT CONTAINS THE NUMBER!
} else {
// IT DOES NOT CONTAIN THE NUMBER
}
Here is perfect solution of your problem
public class Finder {
static int round = 123456789;
static String str = String.valueOf(round);
public static void main(String... args) {
if (str.contains("7")) {
System.out.println("Found");
} else {
System.out.println("Can't found...");
}
}
}
Just convert your integer to String and then try to found the specific value from that string.
You don't have to convert to string in order to search specific digit in integer.
You can use math for that purpose.
Here is the code:
private static boolean isFound(int round) {
while (round > 0) {
if (round % 10 == 7)
return true;
round /= 10;
}
return false;
}
basically what this code do is checking each last digit if it's equals to 7 if not he divides the num by 10 and remove the last digit and after checking again, it will do so until no digit left (num=0) or he will find 7.

Return statement best practices in java?

I want to know the difference between these two codes even though they produce the same output:
CODE 1:
class ret {
public static int add(int x) {
if(x!=0)
return x+add(x-1);
return x;
}
public static void main(String args[]) {
System.out.println(add(5));
}
}
CODE 2:
class ret {
public static int add(int x) {
if(x!=0)
return x+add(x-1);
return 0;
}
public static void main(String args[]) {
System.out.println(add(5));
}
}
They both output 15 but how come the second code also output's 15 instead of zero?My understanding is that the last call would be add(0) for code 2 and it would return zero.I also want to know is it okay to use multiple return statements or use a single return statement and replace the rest with local variables.I remember reading that single entry single exit model is a good practice.
This is a recursive method, so when x != 0, you will return the result of "x added to calling the method again with (x-1)". The final call will always return x == 0 or constant = 0, so you will return 15 from both versions.
Single return vs. multiple return is a matter of debate. The former should be preferred as a rule. Generally it will be obvious where multiple return statements are acceptable as it will be simpler to understand the method with them than with the alternative code constructs required to engineer a single exit point. Also note you could rewrite add as:
public static int add(int x) {
return x == 0 ? 0 : (x + add(x-1));
}
Version 1:
add(5)
call add(4)
call add(3)
call add(2)
call add(1)
call add(0)
return (x = 0)
return (x = 1) + (add(x-1) = 0) = 1
return (x = 2) + (add(x-1) = 1) = 3
return (x = 3) + (add(x-1) = 3) = 6
return (x = 4) + (add(x-1) = 6) = 10
return (x = 5) + (add(x-1) = 10) = 15
Version 2:
add(5)
call add(4)
call add(3)
call add(2)
call add(1)
call add(0)
return (constant = 0) // the only difference
return (x = 1) + (add(x-1) = 0) = 1
return (x = 2) + (add(x-1) = 1) = 3
return (x = 3) + (add(x-1) = 3) = 6
return (x = 4) + (add(x-1) = 6) = 10
return (x = 5) + (add(x-1) = 10) = 15
The use of multiple return statement versus using a single exit point cannot be answered with an easy one-line answer. I guess the best answer you can get is "it depends on your company's standards".
Single exit point is a very good standard, even though I don't personally endorse it. You end up having methods that always have a single return statement at the end, so you never get in a position where you are looking for those many possible return statement while editing someone else's code. I believe that developers that used to code in C tend to follow this standard (see this question).
I, for one, perfer using multiple return statements when it can help simplify the code. One case where I like to use it is to prevent cascading braces in my code. For instance, in the following example:
private int doSomething (int param) {
int returnCode;
if (param >= 0) {
int someValue = param * CONSTANT_VALUE;
if (isWithinExpectedRange (someValue)) {
if (updateSomething (someValue)) {
returnCode = 0;
} else {
returnCode = -3;
}
} else {
returnCode = -2;
}
} else {
returnCode = -1;
}
return returnCode;
}
I find this type of coding to be very confusing when reading it. I tend to change this type of code to:
private int doSomething (int param) {
if (param < 0) {
return -1;
}
int someValue = param * CONSTANT_VALUE;
if (!isWithinExpectedRange (someValue)) {
return -2;
}
if (!updateSomething (someValue)) {
return -3;
}
return 0;
}
The second example looks cleaner, and clearer, to me. Even more when the actual code has some extra coding in the else blocks.
Again, this is personal tastes. Some company might enforce a single exit point, some might not, and some developers prefer single exit point. The bottom line is, if there's a guideline available for you to follow in your environment, then do so. If not, then you can chose your own preference base partly on these arguments.

Sorting of weights(gm, milligrm, litre, mili litre.)

In a column "Size", I have weights like 100mL , 0.5mg, 1L, 2500cm2. I need to sort it out according to ml, mg, gram, litre, cm etc. Please give me solution for it, I have tried with String Comparison but its not worthable as we have to consider weights.
public static int safeCompareIgnoreCase(String name1, String name2) {
if (name1 == name2) {
return 0;
}
if (name2 == null) {
return -1;
}
if (name1 == null) {
return 1;
}
return name1.compareToIgnoreCase(name2);
}
public int compare(InventorySearchResultRecord r1, InventorySearchResultRecord r2) {
int result = safeCompareIgnoreCase(r1.getSize(), r2.getSize());
if (result != 0) {
return result;
}
}
Hint: - You should not compare two values in different units.. Think yourself -> How will you tell whether 1 Dollar is greater or 50 Rupees?? You need to convert them into same units..
And first try to do it on paper.. Get your work done without computer.. If you got the answer, convert it to code.. It would be easier for you..

Categories

Resources