1

Let us say I have two collection called desiredCompanies as an array and companiesSource as List of Companies:

   List<String>  desiredCompanies  = Arrays.asList("AAA", "AAB", "AAC");

   List<Company>  companiesSource  = new ArrayList<Company>();

   Company item1= new Company();
   item1.setCode("AAB");
   item1.setPercent(200);
   item1.setLocation("America");
   companiesSource.add(item1);

   Company item2= new Company();
   item2.setCode("AAX");
   item2.setPercent(900);
   item2.setLocation("Africa");
   companiesSource.add(item2);

   Company item3= new Company();
   item3.setCode("AAC");
   item3.setPercent(900);
   item3.setLocation("Singapore");
   companiesSource.add(item3);

   Company item4= new Company();
   item4.setCode("AAA");
   item4.setPercent(900);
   item4.setLocation("Singapore");
   companiesSource.add(item4);

   Company item5= new Company();
   item5.setCode("AAR");
   item5.setPercent(900);
   item5.setLocation("Japan");
   companiesSource.add(item5);

I would like to get a resulting list with a new List of Company or sort the existing companiesSource as in the order of desiredCompanies which should only contain the item exist in the desiredCompanies list.

1615903
  • 32,635
  • 12
  • 70
  • 99
Bulu
  • 1,351
  • 3
  • 16
  • 37
  • 3
    You would create a `Comparator` that compares Companies based on `desiredCompanies.indexOf(company.getCode())`. Full answer available here: [Comparator based on a configurable order](http://stackoverflow.com/questions/5477511/comparator-based-on-a-configurable-orderl). – aioobe Nov 12 '15 at 08:03
  • ``desiredCompanies`` doesn't contain ``"AAR"`` and ``"AAX"``, for a proper sorting we need details... – Binkan Salaryman Nov 12 '15 at 08:13
  • 1
    Will there ever by a repeated code? Eg, more than one "AAR" company? – matt Nov 12 '15 at 08:31
  • No The company code or AAR are never repeated – Bulu Nov 12 '15 at 08:33

5 Answers5

1

If you want that sorted list has an element for each of the codes in desiredCompanies then the below code will do it. This will have a null value in sortedList for a nonexistent code in companiesSources

List<Company> sortedList = new ArrayList<>();
Map<String, Company> map = new HashMap<>();
for(Company company : companiesSource){
    map.put(company.getCode(), company);
}
for(String desired : desiredCompanies){       
    sortedList.add(map.get(desired));
}

In you want to avoid null values in the sorted collection for non-existent Company refereces from desiredCompanies, then use the code below:

List<Company>  sortedList  = new ArrayList<Company>();
Map<String, Company> map = new HashMap<>();
for(Company company : companiesSource) {
   map.put(company.getCode(), company);
}
for(String desired : desiredCompanies) {
    if(map.get(desired) != null) {
        sortedList.add(map.get(desired));
    }
}
awsome
  • 2,143
  • 2
  • 23
  • 41
  • `sortedList.add(map.get(desired));` will add some `null` values to the collection though. – Zilvinas Nov 12 '15 at 08:24
  • @Zilvinas only for non-existent company codes in ``desiredCompanies`` it will add ``null`` pointers – Binkan Salaryman Nov 12 '15 at 08:26
  • Agree with @Zilvinas – Bulu Nov 12 '15 at 08:28
  • @BinkanSalaryman well that's the point of this filtering - you need to _not to add_ the values which don't have a record in the _codes_ collection. – Zilvinas Nov 12 '15 at 08:35
  • @Zilvinas, it really depends on the requirement what he needs, if he needs to do something like sortedList.get(desiredCompanies.indexOf("ABC")) then null values in the collection does the job.. we can only guess – awsome Nov 12 '15 at 09:07
0

If you are sure that the code is always a string, you could use the indexOf to get the order ( not very efficient, though ):

    final List<String> desiredCompanies = Arrays.asList( "AAA", "AAB", "AAC" );

    List<Company> companiesSource = new ArrayList<Company>();

    /* compute & remove unneeded values here */

    Collections.sort( companiesSource, new Comparator<Company>() {
        @Override
        public int compare( Company o1, Company o2 ) {
            int order1 = desiredCompanies.indexOf( o1.getCode() );
            int order2 = desiredCompanies.indexOf( o2.getCode() );
            return Integer.signum( order1 - order2 );
        }
    } );

To make this more efficient, you could pre-calculate the orders into anther set.

Zilvinas
  • 5,518
  • 3
  • 26
  • 26
  • 1
    Why don't you simply use [``String#compareTo(String)``](http://docs.oracle.com/javase/8/docs/api/java/lang/String.html#compareTo-java.lang.String-) instead? – Binkan Salaryman Nov 12 '15 at 08:16
  • @BinkanSalaryman to compare what? We are comparing the order of strings ( codes ) in the list, nobody said that they will be originally sorted. – Zilvinas Nov 12 '15 at 08:25
  • ``Collections.sort(companiesSource, (o1, o2)->o2.getCode().compareTo(o1.getCode()));``, as ``desiredCompanies`` seems to reflect sorting by alphabet... – Binkan Salaryman Nov 12 '15 at 08:29
  • @BinkanSalaryman I agree it would make sense if we knew in advance that the codes are sorted. – Zilvinas Nov 12 '15 at 08:32
0

Java 8 style:

List<Company> sortedDesiredCompanies = companiesSource.stream()
    .filter(c -> desiredCompanies.contains(c.getCode())).sort()
    .collect(Collectors.toList());
Andrei Amarfii
  • 685
  • 1
  • 6
  • 17
0

You could filter out companies based on their code being in the companiesSource list. You could then sort them according to the index of that code by implementing your own Comparator, and then put it all together with Java 8's streaming APIs.

The Comparator:

public class DesirabilityComparator implements Comparator<Company> {
        private List<String> desiredCompanies;
        public DesirabilityComparator(List<String> desiredCompanies) {
            this.desiredCompanies = desiredCompanies;
        }

        @Override
        public int compare(Company c1, Company c2) {
            return Integer.compare(desiredCompanies.indexOf(c1.getCode()),
                                   desiredCompanies.indexOf(c2.getCode()));
        }
    }

Putting it all together:

List<Company> sortedAndFilteredCompanies =
        companiesSource.stream()
                       .filter(c -> desiredCompanies.contains(c.getCode()))
                       .sorted(new DesirabilityComparator(desiredCompanies))
                       .collect(Collectors.toList());

EDIT:
As aioobe commented, this can be done much more elegantly with Comparator.comparing:

List<Company> sortedAndFilteredCompanies =
        companiesSource.stream()
                       .filter(c -> desiredCompanies.contains(c.getCode()))
                       .sorted(Comparator.comparing(c -> desiredCompanies.indexOf(c.getCode()))
                       .collect(Collectors.toList());
Mureinik
  • 297,002
  • 52
  • 306
  • 350
  • You should be able to replace the whole `DesirabilityComparator` with `Comparator.comparing(c -> desiredCompanies.indexOf(c.getCode())`. – aioobe Nov 12 '15 at 09:50
  • @aioobe good point, thanks! Still getting used to Java 8... I'll edit this into the answer. – Mureinik Nov 12 '15 at 12:24
0

Though it probably isn't productive code, I suggest breaking down the code to logical units, e.g.:

public static void main(String[] args) {
    // register companies by their codes to look them up
    // alternativly, you can implement this in a ``Company(String code)`` constructor
    // along with a static accessible registry map in ``Company``
    Map<String, Company> companyRegistry = new HashMap<>();
    for(Company c : getCompanies()) {
        companyRegistry.put(c.getCode(), c);
    }

    // execute query on model and print out results
    System.out.println(queryCompanies(companyRegistry, "AAA", "AAB", "AAC"));
}

static List<Company> getCompanies() {
    // collect all companies you are going to use later
    List<Company> result = new ArrayList<>();
    Company c;

    // better add a constructor/factory method as shortcut
    // or load the objects from a database
    result.add(c = new Company());
    c.setCode("AAB");
    c.setPercent(200);
    c.setLocation("America");

    result.add(c = new Company());
    c.setCode("AAX");
    c.setPercent(900);
    c.setLocation("Africa");

    result.add(c = new Company());
    c.setCode("AAC");
    c.setPercent(900);
    c.setLocation("Singapore");

    result.add(c = new Company());
    c.setCode("AAA");
    c.setPercent(900);
    c.setLocation("Singapore");

    result.add(c = new Company());
    c.setCode("AAR");
    c.setPercent(900);
    c.setLocation("Japan");

    return result;
}

static List<Company> queryCompanies(Map<String, Company> companyRegistry, String... desiredCompanies) {
    // add "-ea" as VM-option to enable assertions
    // this is just a friendly check to avoid duplicates, you may remove it
    assert new HashSet<>(Arrays.asList(desiredCompanies)).size() == desiredCompanies.length : "desiredCompany contains duplicates";

    List<Company>  result  = new ArrayList<>((int)desiredCompanies.length);

    for(String desiredCompany : desiredCompanies) {
        Company desired = companyRegistry.get(desiredCompany);
        if(desired != null) {
            result.add(desired);
        }
    }
    return result;
}
Binkan Salaryman
  • 3,008
  • 1
  • 17
  • 29