CostLy10
CostLy10

Reputation: 65

Build a sorted list based on multiple parameters?

I have three arrays

String[] persons = {"jack","james","hill","catnis","alphonso","aruba"};
int[] points = {1,1,2,3,4,5};
int[] money = {25,66,24,20,21,22};

The nth position in all three arrays belong to the same entity, for eg:-

persons[0] == points[0] == money[0] i.e jack has 1 point and 25 bucks.

I want to build a list that sorts person alphabetically(ascending) , if the starting letter is same , then it should check points(descending) and if those are same too then it must check the money(descending).

The final list after sorting should be {aruba , alphonso , catnis , hill , james , jack}.

Upvotes: 1

Views: 115

Answers (4)

Marcelo Morgade
Marcelo Morgade

Reputation: 127

If you're up to something quick and dirty:

    Comparator<Integer> cName = (i, j) -> Character.compare( persons[i].charAt(0), persons[j].charAt(0));
    Comparator<Integer> cPoints = (i, j) -> Integer.compare( points[i], points[j]);
    Comparator<Integer> cMoney = (i, j) -> Integer.compare( money[i], money[j]);

    List<String> l = 
            IntStream.range(0, persons.length).boxed()
            .sorted( cName.thenComparing(cPoints.reversed()).thenComparing(cMoney.reversed()) )
            .map( i -> persons[i] )
            .collect(Collectors.toList());

    System.out.println(l);

The first 3 lines use lambdas to define comparators based on arrays indexes.

The following line uses streams:

  1. Create an int stream of indexes from 0 to persons.length-1
  2. Sort indexes of the stream based on the sequence of comparators
  3. Map sorted indexes to person names
  4. Collect it into a List

Ain't lambda and streams cool?

Upvotes: 3

John Kugelman
John Kugelman

Reputation: 361635

Similar to EvanM's answer, you should group the three pieces of data into a single class.

public class Person {
   private String name;
   public String getName() { return name; }

   private int points;
   public int getPoints() { return points; }

   private int money;
   public int getMoney() { return money; }
}

Then you could sort them like so:

List<Person> persons = ...;

persons.sort(Comparator
    .comparing(p -> p.getName().charAt(0))
    .thenComparing(Comparator.comparing(Person::getPoints).reversed())
    .thenComparing(Comparator.comparing(Person::getMoney) .reversed())
);

Upvotes: 0

user4695271
user4695271

Reputation:

If you can have a Person model:

final class Person {
  private final String name;

  private final int points;

  private final int money;

  public Person(final String name, final int points, final int money) {
    this.name = name;
    this.points = points;
    this.money = money;
  }

  // getters and setters (if you want)

  @Override
  public String toString() {
    final StringBuffer sb = new StringBuffer("Person {")
        .append("name=")
        .append(name)
        .append(", points=")
        .append(points)
        .append(", money=")
        .append(money)
        .append('}');
    return sb.toString();
  }
}

Then you could do something like this:

public static void main(final String... args) throws Exception {
  Person[] persons = new Person[6]; // Use a List (if you can)
  persons[0] = new Person("jack", 1, 25);
  persons[1] = new Person("james", 1, 66);
  persons[2] = new Person("hill", 2, 24);
  persons[3] = new Person("catnis", 3, 20);
  persons[4] = new Person("alphonso", 4, 21);
  persons[5] = new Person("aruba", 5, 22);
  System.out.printf("persons = %s%n%n", Arrays.toString(persons));
  System.out.printf("Person[0] = %s%n%n", persons[0]);
  Collections.sort(Arrays.asList(persons), (c1, c2) -> {
    final int charComp = Character.compare(c1.name.charAt(0), c2.name.charAt(0));
    if (0 == charComp) {
      final int pointsComp = Integer.compare(c2.points, c1.points);
      if (0 == pointsComp) { return Integer.compare(c2.money, c1.money); }
      return pointsComp;
    }
    return charComp;
  });
  // The collection was modified at this point because of the "sort"
  System.out.printf("persons = %s%n", Arrays.toString(persons));
}

Results:

persons = [Person {name=jack, points=1, money=25}, Person {name=james, points=1, money=66}, Person {name=hill, points=2, money=24}, Person {name=catnis, points=3, money=20}, Person {name=alphonso, points=4, money=21}, Person {name=aruba, points=5, money=22}]

Person[0] = Person {name=jack, points=1, money=25}

persons = [Person {name=aruba, points=5, money=22}, Person {name=alphonso, points=4, money=21}, Person {name=catnis, points=3, money=20}, Person {name=hill, points=2, money=24}, Person {name=james, points=1, money=66}, Person {name=jack, points=1, money=25}]


A more compact sort (but a little bit less efficient since you have to run all comparisons upfront):

Collections.sort(Arrays.asList(persons), (c1, c2) -> {
  final int names = Character.compare(c1.name.charAt(0), c2.name.charAt(0));
  final int points = Integer.compare(c2.points, c1.points);
  final int money = Integer.compare(c2.money, c1.money);
  return (0 == names) ? ((0 == points) ? money : points) : names;
});

Upvotes: 2

EvanM
EvanM

Reputation: 687

So I think you want something like this:

public class Person {
   String name;
   int points;
   int money;

   public Person(String name, int points, int money) {
       this.name = name;
       this.points = points;
       this.money = money;
   }

   // getters
}

Then create a List<Person> with the data you have (e.g., new Person("jack", 1, 25)). And then sort them:

Collections.sort(persons, (person1, person2) -> {
    // could be written more concisely, but this should make things clear
    char letter1 = person1.getName().charAt(0);
    char letter2 = person2.getName().charAt(0);
    if (letter1 != letter2) {
        return letter1 - letter2;
    }
    int points1 = person1.getPoints();
    int points2 = person2.getPoints();
    if (points1 != points2) {
        return points2 - points1; // notice the order is reversed here
    }
    int money1 = person1.getMoney();
    int money2 = person2.getMoney();
    if (money1 != money2) {
        return money2 - money1;
    }
    return 0; // unless you want to do something fancy for tie-breaking
});

That will give you a sorted List<Person> according to your criteria.

Upvotes: 3

Related Questions