Insertion Sorting compareTo() - java

So I have a list of objects that I would like to sort based on the trait of each name of the object (In alphabetical order). We are required to have it "Roughly" in alphabetical order, so I have what I thought was a mediocre implementation of the insertion sorting algorithm. However, I'm only able to sort one item properly alphabetically. I have been at this for hours and seem to have hit a road block.
public void sort(){
int i=1;
while(list[i]!=null) { //while there is an element in the array
String one=list[i-1].getName(); //get the name of the "first object"
String two=list[i].getName(); //get the name of the "second object"
if(two.compareTo(one)==0){ // if equal move on
i++;
}
else if(two.compareTo(one)<0){// two is before one
Contact temp =list[i-1]; //store this temporarily
list[i-1]=list[i]; //swap them
list[i]=temp; //put temp back where it belongs
i++; //check next elements
}
i++
}
}
Here is what is list, before, and after the sort...http://image.prntscr.com/image/698cf44309ee43c29532ebe71a4925fe.png

First, you have to understand how insertion sort works.
You pick an element from the unsorted half of the list (O(n))
Try to insert the newly picked element to the sorted half of the list (O(n))
So, insertion sort is O(n^2), meaning it requires a nested loop, at least.
Here is a modified version:
int i = 1;
while(list[i] != null) {
// i divides sorted and unsorted
// try to insert i to the right place, so loop j from i-1 to 0
int j = i;
while (j > 0) {
String one = list[j-1].getName();
String two = list[j].getName();
int cmp = one.compareTo(two);
if (cmp <= 0) {
break;
}
Contact temp = list[j-1];
list[j-1] = list[j];
list[j] = temp;
--j;
}
++i;
}

There are many ways to implement insertion sort. I'll explain you a simpler way by which you can sort your array of objects by name as you have explained.
The key properties of Insertion sort is:
Insertion sort starts from he left end of the array and advances to
right.
This sorting mechanism will not look into elements on the right,
rather it focuses on the current encountered item and drags back
that item to it's correct position.
Let's code up keeping these things in mind!!
/*
* Performs Insertion sort on given array of contacts!!
*/
public static Contact[] sortContacts(Contact[] contacts){
int n = contacts.length;
// Advance the pointer to right
for(int i = 0; i < n; i++){
/*
* Compare current item with previous item.
* If current item is not in it's correct position,
* swap until it's dragged back to it's correct position.
* (Ascending order!!)
*/
for(int j = i; j > 0; j--){
if(less(contacts[j], contacts[j - 1])){
swap(contacts, j, j - 1);
}else{
break;
}
}
}
return contacts;
}
private static boolean less(Contact a, Contact b){
return a.getName().compareTo(b.getName()) < 0;
}
private static void swap(Contact[] contacts, int i, int j){
Contact temp = contacts[i];
contacts[i] = contacts[j];
contacts[j] = temp;
}
These three methods facilitates your sorting!!
Now to test it, let's create some contacts and sort them!!
Contact one = new Contact();
one.setName("tony");
one.setPhone("6666");
one.setMail("kkk#lll.com");
Contact two = new Contact();
two.setName("steve");
two.setPhone("777");
two.setMail("rrr#mmm.com");
Contact three = new Contact();
three.setName("clint");
three.setPhone("333");
three.setMail("ggg#sss.com");
Contact four = new Contact();
four.setName("bruce");
four.setPhone("222");
four.setMail("bbb#ccc.com");
Let's sort:
Contact[] res = Insertion.sortContacts(new Contact[]{one,two,three,four});
for(Contact c : res){
System.out.println(c);
}
This produces the output:
Contact [name=bruce, mail=bbb#ccc.com, phone=222]
Contact [name=clint, mail=ggg#sss.com, phone=333]
Contact [name=steve, mail=rrr#mmm.com, phone=777]
Contact [name=tony, mail=kkk#lll.com, phone=6666]
Which is sorted based on names of each contact!!
I hope this is what you were trying to achieve

You can use interface Comparable for your class.
Example:
public class Contact implements Comparable<Contact>
// ...
public int compareTo(Contact other) {
return getName().compareTo(other.getName());
}
}
Then if list is an array:
Arrays.sort(list);

Related

How to sort an array without losing reference to the index thingy?

Okay. I'm effectively a -total- beginner to coding (taken some classes but a slow/dense learner) and the answer is probably ridiculously simple. I have one string array that has names and another that has the scores associated.
names[0] = blinky
scores[0] = 42 (blinky's score)
names[1] = inky
scores[1] = 37 (inky's score)
in the for loop i calls the number (index number? I'm horrible with terms. The only thing that ever seems to make sense is the code itself). Anyway, I want to be able to preserve i.
I want to make a list that puts the names with the scores in order from highest to lowest.
I don't know if using util.Arrays or anything that will automatically sort will help. I believe I'll have to manually have to sort them in order to keep the names and numbers aligned
//Example
String[] names = {"Blinky","Inky","Pinky","Clyde"};
int[] scores = {42,37,67,50};
for (int = 0; i < scores.length; i++){
System.out.println("what do?")
}
How would I go about making a list that puts the names in order? Simpler the better.
I'd be very appreciative of help.
Edit: I'd like to thank all of you for your help! :)
To get the results below, you can try out the following codes.
Unsorted
---------
Blinky, score=42
Inky, score=37
Pinky, score=67
Clyde, score=50
sort by score ascending
Inky, score=37
Blinky, score=42
Clyde, score=50
Pinky, score=67
sort by Name ascending
Blinky, score=42
Clyde, score=50
Inky, score=37
Pinky, score=67
1) create a new class called NameScoreEntity to store both variables together (this way when you sort, both the names and scores are done at the same time)
public class NameScoreEntity {
String name;
int score;
public NameScoreEntity(String name, int score) {
this.name = name;
this.score = score;
}
#Override
public String toString() {
return name + ", score=" + score;
}
}
2) you then create an arrayList of your new class to copy your two arrays into.
String[] names = {"Blinky","Inky","Pinky","Clyde"};
int[] scores = {42,37,67,50};
List<NameScoreEntity> data = new ArrayList<>(); // your data is now stored here
for (int i= 0; i < scores.length; i++){
data.add(new NameScoreEntity(names[i], scores[i]));
}
//print unsorted
System.out.println("Unsorted\n---------");
for (NameScoreEntity e : data) {
System.out.println(e);
}
3) finally, you just use the sort() method that is available in the list to do your sorting.
System.out.println("sort by score ascending");
data.sort(new Comparator<NameScoreEntity>() {
#Override
public int compare(NameScoreEntity o1, NameScoreEntity o2) {
return Integer.compare(o1.score, o2.score); //for descending just swap o1 & o2
}
});
for (NameScoreEntity e : data) {
System.out.println(e);
}
System.out.println("sort by Name ascending");
data.sort(new Comparator<NameScoreEntity>() {
#Override
public int compare(NameScoreEntity o1, NameScoreEntity o2) {
return o1.name.compareTo(o2.name) ;//for descending just swap o1 & o2
}
});
for (NameScoreEntity e : data) {
System.out.println(e);
}
You should use a TreeMap, which will always keep the orders between your scores and match the corresponding String.
// new TreeMap that sorts from highest to lowest
Map<Integer, String> map = new TreeMap<>(Collections.reverseOrder());
Then just put all your values into the Map using the loop you already have:
for(int i = 0; i < scores.length; i++)
map.put(scores[i], names[i]);
And finally you can just print the Map:
System.out.println(map);
Result:
{67=Pinky, 50=Clyde, 42=Blinky, 37=Inky}

how to input values into ArrayList alphabetically

I'm given the task of adding a value into an ArrayList alphabetically (by lastname first, and firstname if lastnames are the same),I'm not allowed to sort the ArrayList once all the inputs are given. What I am suppose to do is determine the location of where the value should be each time an input is given and place it there. Example values: Inputs are {John Doe, Bryan Sully, Harry Ache, Ali Doe, Bry Dyre} , Output should be {Harry Ache, Ali Doe, John Doe, Bry Dyre, Bryan Sully}. However, in cases where there are two of the same last names, what will occur is {Harry Ache, Ali Doe, Bry Dyre, John Doe, Bryan Sully}, why?
ArrayList<Person> list = new ArrayList<Person>();
public void addPerson(Person p){
if(list.size() == 0){
list.add(p);
} else{
for(Person pp: list){
int getIndex = list.indexOf(pp);
int lOrder = p.getLastName().compareTo(pp.getLastName());
if(lOrder > 0){
list.add(getIndex + 1, p);
break;
} else if(lOrder == 0){
int fOrder = p.getFirstName().compareTo(pp.getFirstName());
if(fOrder > 0){
list.add(getIndex, p);
break;
} else {
list.add(getIndex + 1, p);
break;
}
} else {
list.add(getIndex, p);
break;
}
}
}
}
One issue with your code is that you are making an insertion after a single iteration of your loop, inserting Person p only in comparison to the first Person in the list.
Here is an improved implementation:
public void addPerson(Person person) {
if (list.isEmpty()) {
list.add(p);
} else {
// the position where you will insert the Person;
// default value is list.size(), so it can be added to the end
int position = list.size();
Person p;
for (int i = 0; i < list.size(); i++) {
p = list.get(i);
// if the comparison result is > 0, you found the first Person
// in the list that comes alphabetically after;
// Insert person before it, at position i
if (compare(p, person) > 0) {
position = i;
break; // only break now that you've found the insert position
}
}
list.add(person, position);
}
}
/**
* #return 0 if both Persons p1 & p2 have the same names,
* a positive number if p1 is lexicographically greater than p2,
* or a negative number if p1 is lexicographically less than p2
*/
private int compare(Person p1, Person p2) {
int result = p1.getLastName().compareTo(p2.getLastName());
if (result == 0) {
result = p1.getFirstName().compareTo(p2.getFirstName());
}
return result;
}
As others suggested, implementing the Comparable interface in Person if possible can also help create a cleaner implementation.
Few improvements.
You are better off if you can iterate the list using the index like
for(int i = 0; i < array.size(); i++)
You need not have to check for lastName and firstName separately. You can just compare both of them together (p.lastName + p.firstName).compareTo(pp.lastName + pp.firstName) and act upon the result. As you already have the index of the current element, you just have to check if you just insert the new item on the same index or not.
Here is a sample implementation
static void addPerson(Person person) {
for (int i = 0; i < sortedList.size(); i++) {
var existingPerson = sortedList.get(i);
var compareResult = compare(existingPerson, person);
if (compareResult > 0) {
sortedList.add(i, person);
return;
}
}
sortedList.add(person);
}
static int compare(Person p1, Person p2) {
return (p1.lastName + p1.firstName).compareTo(p2.lastName + p2.firstName);
}
The problem with your algorithm, is that you always looking at the first person in the target list, and making decisions solely on that person, notice how all your "branches" have a break statement at the end?
I understand your "reason" for adding all those break statements, those make sure the new person always gets inserted into the list, when it doesn't match, the same reason why your first check if the list is empty.
To properly implement an "insertion sort" system, you actually need to loop over all the indexes of your list.
An simple "insertion sort" algorithm looks like this:
Loop over the target list
Check if existing entry is "bigger" than the newly person
If the existing entry is bigger, we need to move that entry (all all remaining entries) up 1 spot, insert out entry, and abort
If we are done looping, and we didn't abort in the above steps, insert our entry at the end of the list.
ArrayList<Person> list = new ArrayList<Person>();
public void addPerson(Person p){
// Loop
int size = list.size();
for(int getIndex = 0; getIndex < size; getIndex++){
Person pp = list.get(getIndex);
// Compare
int compare = p.getLastName().compareTo(pp.getLastName());
if (compare > 0) {
continue;
} else if(compare == 0) {
if (p.getFirstName().compareTo(pp.getFirstName()) > 0) {
continue;
}
}
// Add in existing slot
list.add(getIndex, p);
return;
}
// Add at the end
list.add(p);
}
One hard part of this, is the fact that we have to compare on 2 things for a single person, this makes the compare logic way harder than it needs to be. This can be solved by factoring this part out to a "helper" comparator that does this logic merging for us:
Comparator<Person> compare = Comparator
.comparing(Person::getLastName)
.thenComparing(Person::getFirstName);
// Then, inside the above loop:
...
// Compare
if (compare.compare(p, pp) > 0) {
continue;
}
...

Selection sorting an instance of an object of type ArrayList of objects giving weird results

I have a CarLot class, a Car class, and a CarLotApp class to set up a GUI for it. The CarLot is an ArrayList of Car objects. I have methods in CarLot that selection sort the CarLot based off of instance variables in Car (make, model, mpg,etc.)
For example:
public ArrayList<Car> getSortedDescMPG() {
ArrayList<Car> lotSortedByMPG = new ArrayList<Car>(myCars);
Car car;
for (Car c : lotSortedByMPG) {
double currentMax = c.getMPG();
car = c;
int currentMaxIndex = lotSortedByMPG.indexOf(c);
for (Car c2 : lotSortedByMPG) {
if (currentMax < c2.getMPG()) {
currentMax = c2.getMPG();
car = c2;
currentMaxIndex = lotSortedByMPG.indexOf(c2);
}
}
if (currentMaxIndex != lotSortedByMPG.indexOf(c)) {
lotSortedByMPG.set(currentMaxIndex, c);
lotSortedByMPG.set(lotSortedByMPG.indexOf(c), car);
}
}
return lotSortedByMPG;
}
I'm trying to get the sorted list into a TextArea in CarLotApp. In CarLotApp I also have three buttons, one to add a car, one to sort asc and one to sort desc, and a combo box to choose what instance variable to sort by.
class SortDesc extends WidgetViewerActionEvent {
#Override
public void actionPerformed(ActionEvent event) {
txtrSortedCarLot.setText("");
if (cmbSortOptions.getSelectedIndex() == 0)
txtrSortedCarLot.setText(myCarLot.toString());
else if (cmbSortOptions.getSelectedIndex() == 1)
txtrSortedCarLot.setText(CarLot.toString(myCarLot.getSortedDescMPG()));
else if (cmbSortOptions.getSelectedIndex() == 2)
etc...
}
}
The main issue is that the ArrayLists that were spat out from my sorting methods were out of order. I looked for similar posts but they all linked to one that focused on using a comparator to sort. I want to do this without useing compartors. So then I didn't really know what to search for since for what I can tell my sorting methods should work. So I don't know what I'm doing wrong.
When you do selection sort, the beginning of the array or list is becoming sorted, and you shouldn't look there for a maximum value. You rather want to find the maximum value in the remainder of the list, which is still unsorted. Your code was instead repeatedly finding the same car with maximum MPG.
Here's a working selection sort:
public static ArrayList<Car> getSortedDescMPG() {
ArrayList<Car> lotSortedByMPG = new ArrayList<>(myCars);
for (int i = 0; i < lotSortedByMPG.size(); i++) {
// At the beginning of each iteration, cars 0 through (i-1) are sorted
// Find the max-MPG car with index in the range (i, size-1) inclusive
Car carI = lotSortedByMPG.get(i);
int maxIndex = i;
Car maxCar = carI;
int maxMPG = carI.getMPG();
for (int j = i + 1; j < lotSortedByMPG.size(); j++) {
Car curCar = lotSortedByMPG.get(j);
int curMPG = curCar.getMPG();
if (curMPG > maxMPG) {
maxIndex = j;
maxCar = curCar;
maxMPG = curMPG;
}
}
// Now swap the max-MPG car (at index maxIndex) with car i (at index i)
lotSortedByMPG.set(i, maxCar);
lotSortedByMPG.set(maxIndex, carI);
}
return lotSortedByMPG;
}

Sorting an integer array whilst it has a String array for a scoreboard

Im making a scoreboard for a game that im creating but I need to make a scoreboard which will sort the scores in decesding order using a bubble sort algorithim. However, the corresponding names of the sorted score list would be muddled up. I have searched for an answer but they are done with array.sort which isnt my own sorting algorithim.
Please help me sort the scores while the correct names will be printed along side them.
for now lets say:
int[] scores={6,3,15,12};
String[] names={"player1","player2","player3","player4"};
public static void results(String [] names, int [] results) {
int temp;
boolean sorted=false;
while(sorted==false) {
sorted = true;
for(int i=0; i<results.length-1; i++) {
if(results[i] < results[i+1]) {
temp = results[i];
results[i]=results[i+1];
results[i+1] = temp;
sorted = false;}
}
}
for(int i=0; i<results.length; i++) {
print("In position " + (i+1) + " is " + names[i] + " with " + results[i] + " points.");}
}
public static void resultscalc(int [] score, int [] results) {
for(int i=0; i<score.length; i++) {
results[i] = score[i]; }
}
If you really have to do it like that you could just add another temp variable and swap the items in names at the same time you swap the results. ie. just duplicate the first 3 lines in the if statement.
But it would be better to make a Player class that has a name and a result, then just sort an array of players.
Would look something like this
if(players[i].getResult() < players[i+1].getResult()) {
temp = players[i];
players[i]=players[i+1];
players[i+1] = temp;
sorted = false;}
}
and then just players[i].getName() for the print statements.
Use object oriented programming. It's "difficult" to maintain parallel arrays. It's much easier to create a data type, say Player which holds the name and score and then implement the Comparable interface and sort based on that (or just use your custom sort to sort on the score values of the Player object).

Custom sort with insertion sort

I've gotten the basics of insertion code but I'm stuck on how to compare it with another condition other then (list by A-Z, Z-A)
for the example a list of staff ranks were given :
public static final String[] staffrank = {
"Trainee",
"Junior",
"Senior",
"Administrator"
};
I have a method to compareRank
If it returns 0 means they're of equal rank (staff 1 and staff 2)
if it returns -1 means staff 1 is lower rank than staff 2
if it returns 1 means staff 1 is higher rank than staff 2
Then I have a list of staffs in void main
Staff[] snames;
int countname=0;
snames = new Staff[50];
snames[countname++] = new Staff("Amy","Trainee");
snames[countname++] = new Staff("Annie","Junior");
snames[countname++] = new Staff("Tom","Administrator");
snames[countname++] = new Staff("Dave","Trainee");
snames[countname++] = new Staff("Gary","Junior");
snames[countname++] = new Staff("Donna","Senior");
then the insertion sort compare code
public static void insertionSortbyRank(Staff[] snames, int countname) {
//insertion sort
for(int i =1; i < countname; i++) {
int j = i;
int comparerank = Staff.compareRank(snames[j],snames[j-1]);
String name = snames.getName();
String rank = snames.getRank();
//if staff is lower rank
if(comparerank==-1) {
Then i'm unsure what to put in this while loop
still giving me an unsorted list
while( j >0 && rank.compareRank(list[j], list[j - 1]) == 1))) {
list[j].rank =[j-1].rank;
list.[j].name = [j-1].name;
j--;
}
then the end is replacing the new values
snames[j].name = name;
snames[j].rank = rank;
the output suppose to give : (by order of their ranks from low to highest according to the chart)
Amy, Trainee
Dave, Trainee
Annie, Junior
Gary, Junior
Donna, Senior
Tom, Administrator
Any help would be appreciated..thank you
I would assume that since you've been given a staffrank variable, that you should probably make use of it. However, aside from mentioning it at the top of your question, you make no other use of it that we've been shown...So my assumption is that you've ignored it completely.
I think that's the key to your puzzle here.
Note that I'm ignoring the fact that String arrays are not the best representation (I'd go for an enum or something like a class...Potentially implementing Comparable<StaffRank> somewhere...But this is a test question as noted, after all...)
You could make a function like this:
public static final int compareRank(String[] ranks, String rank) {
for (int i=0; i<ranks.length; i++) {
String string = ranks[i];
if string.equals(rank) return i;
}
return -1;
}
Which will return an integer between -1 and (ranks.length -1) which you can use for comparison. The smaller the number the more junior the rank.
Inside your while loop you'll have to compare each staff and swap them if the "i"-th staff is greater than the "i+1"-th staff.
for (int i=0; i<(snames.length-1); i++) {
Staff sname1 = sname[i];
Staff sname2 = sname[i+1]
//compare sname1 to sname2 and swap if sname1 > sname2
}

Categories