I would like to sort a list of Pojos based on the field (sortField) and order (sortOrder) parameters passed in. I have this working (for 6 fields) -
switch (sortField) {
case "field1" :
if (sortOrder == "asc") {
list.sort((lItem1, lItem2) -> lItem1.getField1().compareTo(lItem2.getField1()));
} else {
list.sort((lItem1, lItem2) -> lItem2.getField1().compareTo(lItem1.getField1()));
}
break;
case "field2" :
if (sortOrder == "asc") {
list.sort((lItem1, lItem2) -> lItem1.getField2().compareTo(lItem2.getField2()));
} else {
list.sort((lItem1, lItem2) -> lItem2.getField2().compareTo(lItem1.getField2()));
}
break;
//...
}
This works perfectly but feels a little bit clunky and I just wondered if anyone could point me to a (relatively simple!) tidier & more graceful way to do it ?
You can create a Map<String, Comparator<MyPojo>> and use it like:
Map<String, Comparator<MyPojo>> comparators = new HashMap<>();
comparators.put("field1", Comparator.comparing(MyPojo::getField1));
comparators.put("field2", Comparator.comparing(MyPojo::getField2));
//...
Comparator<MyPojo> comp = comparators.get(sortField);
list.sort("asc".equals(sortOrder) ? comp : comp.reversed());
I think, there are few things you can consider like reusability and duplicate codes.
Duplicate codes:
if (sortOrder == "asc")
{
list.sort((lItem1, lItem2) -> lItem1.getField2().compareTo(lItem2.getField2()));
}
else
{
list.sort((lItem1, lItem2) -> lItem2.getField2().compareTo(lItem1.getField2()));
}
To solve this, I would write either two separate methods to do sorting in asc or desc separately or write a single method to do both with parameter to define sorting order.
Solutions
Example1:
public List sortedList(List items, boolean isOrderAscending) { //do stuffs with items based on isOrderAscending and return sorted items}
Example2:
public List sortedListInAscendingOrder(List items) { //return ascending ordered list}
public List sortedListInDescendingOrder(List items) { //return descending ordered list}
I would even write a method to get sorting order from sortOrder.
public boolean isInAscending(String sortOrder) { //return true or false based on sortOrder}
Compiling whole:
List sortedItems = sortedList(items, isOrderAscending(sortedOrder));
Maybe after giving enough thoughts, whole field part can be altered in a more elegant way.
Related
I have the following structure:
class MyClass {
String name;
String descr;
public String getName() {
return name;
}
}
Now I have a List of those objects and I want to print the name from the object above if the list contains any of those elements.
This is my code so far:
List<MyClass> list = getList();
if (list != null && list.size() > 0) {
System.out.println(list.get(0).getName());
} else {
System.out.println("list is empty");
}
This will work when the list contains only one element. Now I need to improve it and consider an example when there is more than one element - in that case I need to print all names, comma separated.
For example the output should be:
When there are 3 elements:
name1,name2,name3
when there's one element:
name1
and when there's none:
list is empty
what's the most efficient way of implementing it?
You could use Collectors.joining combined with Collectors.collectingAndThen:
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.joining;
...
String res =
list.stream()
.map(c -> c.name)
.collect(collectingAndThen(joining(","), s -> s.isEmpty() ? "list is empty" : s));
If you want to take in account the option when the list is null, you could do:
String res =
Optional.ofNullable(list).map(l -> l.stream()...).orElse("list is empty");
but to be honest I would use an if statement beforehand:
if(list == null || list.isEmpty()) {
return "list is empty";
} else {
return list.stream().map(c -> c.name).collect(joining(","));
}
You also can use the built-in class StringJoiner to simplify your code as following code:
StringJoiner joiner = new StringJoiner(",");
joiner.setEmptyValue("list is empty");
list.forEach(it -> joiner.add(it.getName()));
System.out.println(joiner);
i have a Problem and i hope you can help me,
i have a list of SelectItem to ordern
the inital order is:
geschieden
ledig
unbekannt
verheiratet
verwitwet
and i will the list like:
ledig
verheiratet
geschieden
verwitwet
unbekannt
i have the method implementiert but i don t have the right order:
public List getFamilienstandSelectItems()
{
List getFamilienstandSelectItems =TK_Familienstand_DS_Values.getInstance().getSelectItems();
Collections.sort(getFamilienstandSelectItems , new Comparator<SelectItem>()
{
public int compare(SelectItem s1, SelectItem s2)
{
if (s1.getLabel()=="ledig")
{
return 0;}
else if (s1.getLabel()=="verheiratet" )
{ return 0;}
else if (s2.getLabel()=="geschieden" )
{ return 1;}
else if (s2.getLabel()=="unbekannt" )
{ return -1;}
else if (s2.getLabel()=="verwitwet " )
{ return 0;}
else return 1;
} });
return getFamilienstandSelectItems;
}
and the result of this method:
ledig
verheiratet
geschieden
unbekannt
verwitwet
something is missing??
thank you
The compare method should tell us, of the two items that I'm comparing right now, which one should come first? Negative numbers means that the second item should come first, 0 means that they are equal and positive numbers means that the first item should come first.
So give each item type a number and create a function
public int getPriority(SelectItem item) {
// Give higher values to the items that should come first
}
and then do something like this in your compare method
public int compare(SelectItem s1, SelectItem s2) {
return Integer.compare(getPriority(s1),getPriority(s2));
}
I have an array of CLBeacon objects which all have a property .proximity.
I want to order the array by this property which contains the CLProximity enum. So I want all objects to be in order IMMEDIATE, NEAR, FAR, UNKNOWN.
Is there a way to do this neatly without resorting to a bunch of if statements?
If you define a (computed read-only) property sortIndex of CLProximity
extension CLProximity {
var sortIndex : Int {
switch self {
case .Immediate:
return 0
case .Near:
return 1
case .Far:
return 2
case .Unknown:
return 3
}
}
}
then you can sort an array of beacons with
let sortedBeacons = sorted(beacons) { $0.proximity.sortIndex < $1.proximity.sortIndex }
If .Unknown is the only CLProximity value that needs
"special treatment" and all other possible values are in the desired
relative order then you can simplify the property definition to
extension CLProximity {
var sortIndex : Int {
return self == .Unknown ? Int.max : rawValue
}
}
You can use custom comparator and sort an array using that ,
You will "say" for all objects that has "unknown" proximity are "bigger" than others
var sortedArray = persons.sortedArrayUsingComparator {
(obj1, obj2) -> NSComparisonResult in
if obj1.proximity.rawValue == obj12.proximity.rawValue {
return NSComparisonResult.OrderedSame
} else if obj1.proximity == .UNKNOWN || obj1.proximity.rawValue > obj12.proximity.rawValue {
return NSComparisonResult.OrderedDescending
}
return NSComparisonResult.OrderedAscending
}
Based on what Julia wrote above I had cobbled this together:
self.beacons = beacons as! [CLBeacon]
var tempBeacons = zip(self.beacons, self.beacons.map({
(b: CLBeacon) -> Int in
if b.proximity == .Immediate {
return 0
} else if b.proximity == .Near {
return 1
} else if b.proximity == .Far {
return 2
} else if b.proximity == .Unknown {
return 3
}
return 0
}))
self.beacons = sorted(tempBeacons, {$0.1 < $1.1}).map({ $0.0 })
Thanks all!
Based on #Martin answer.
You can also create Int enum and assign value to it and then sort it like below.
enum myEnum: Int {
case A = 0
case B = 1
case C = 2
case D = 3
}
let myData : [myEnum:[String]] = [.C:["3"],.D:["4"],.B:["2"],.A:["1"]]
print(myData.first?.key)
let newData = myData.sorted(by: { $0.key.rawValue < $1.key.rawValue })
print(newData.first?.key)
Hope this helps
Swift 5
Now you can just add Comparable to your enum and it respects the order
enum ContainerLevel: Comparable {
case empty
case almostEmpty
case halfFull
case almostFull
case full
}
//Are we running low?
let needMoreCoffee = coffeeMugLevel > .halfFull
print(needMoreCoffee) //true
Link to more Code examples
While looking though some code of the project I'm working on, I've come across a pretty hefty method which does
the following:
public string DataField(int id, string fieldName)
{
var data = _dataRepository.Find(id);
if (data != null)
{
if (data.A == null)
{
data.A = fieldName;
_dataRepository.InsertOrUpdate(data);
return "A";
}
if (data.B == null)
{
data.B = fieldName;
_dataRepository.InsertOrUpdate(data);
return "B";
}
// keep going data.C through data.Z doing the exact same code
}
}
Obviously having 26 if statements just to determine if a property is null and then to update that property and do a database call is
probably very naive in implementation. What would be a better way of doing this unit of work?
Thankfully C# is able to inspect and assign class members dynamically, so one option would be to create a map list and iterate over that.
public string DataField(int id, string fieldName)
{
var data = _dataRepository.Find(id);
List<string> props = new List<string>();
props.Add("A");
props.Add("B");
props.Add("C");
if (data != null)
{
Type t = typeof(data).GetType();
foreach (String entry in props) {
PropertyInfo pi = t.GetProperty(entry);
if (pi.GetValue(data) == null) {
pi.SetValue(data, fieldName);
_dataRepository.InsertOrUpdate(data);
return entry;
}
}
}
}
You could just loop through all the character from 'A' to 'Z'. It gets difficult because you want to access an attribute of your 'data' object with the corresponding name, but that should (as far as I know) be possible through the C# reflection functionality.
While you get rid of the consecutive if-statements this still won't make your code nice :P
there is a fancy linq solution for your problem using reflection:
but as it was said before: your datastructure is not very well thought through
public String DataField(int id, string fieldName)
{
var data = new { Z = "test", B="asd"};
Type p = data.GetType();
var value = (from System.Reflection.PropertyInfo fi
in p.GetProperties().OrderBy((fi) => fi.Name)
where fi.Name.Length == 1 && fi.GetValue(data, null) != null
select fi.Name).FirstOrDefault();
return value;
}
ta taaaaaaaaa
like that you get the property but the update is not yet done.
var data = _dataRepository.Find(id);
If possible, you should use another DataType without those 26 properties. That new DataType should have 1 property and the Find method should return an instance of that new DataType; then, you could get rid of the 26 if in a more natural way.
To return "A", "B" ... "Z", you could use this:
return (char)65; //In this example this si an "A"
And work with some transformation from data.Value to a number between 65 and 90 (A to Z).
Since you always set the lowest alphabet field first and return, you can use an additional field in your class that tracks the first available field. For example, this can be an integer lowest_alphabet_unset and you'd update it whenever you set data.{X}:
Init:
lowest_alphabet_unset = 0;
In DataField:
lowest_alphabet_unset ++;
switch (lowest_alphabet_unset) {
case 1:
/* A is free */
/* do something */
return 'A';
[...]
case 7:
/* A through F taken */
data.G = fieldName;
_dataRepository.InsertOrUpdate(data);
return 'G';
[...]
}
N.B. -- do not use, if data is object rather that structure.
what comes to my mind is that, if A-Z are all same type, then you could theoretically access memory directly to check for non null values.
start = &data;
for (i = 0; i < 26; i++){
if ((typeof_elem) *(start + sizeof(elem)*i) != null){
*(start + sizeof(elem)*i) = fieldName;
return (char) (65 + i);
}
}
not tested but to give an idea ;)
Trying something with Linq / Lambda's, but have no idea where to search.
I'm working on some simple sorting in an ASP.net GridView. Here's some sample code:
IQueryable<User> query = (from c in users select c).AsQueryable<User>();
if (isAscending)
{
switch (e.SortExpression)
{
case "Name":
query.OrderBy(c => c.Name);
break;
default:
break;
}
}
else
{
switch (e.SortExpression)
{
case "Name":
query.OrderByDescending(c => c.Name);
break;
default:
break;
}
}
grid.DataSource = query.ToList();
grid.DataBind();
I am, however, unsatisfied with the code as it is very sensitive to errors and requires lots of duplicated code. I'm hoping to solve this using Lambda expressions instead, but I have no idea how. Here's what I would like to go to:
IQueryable<User> query = (from c in users select c).AsQueryable<User>();
var param = null;
switch (e.SortExpression)
{
case "Name":
param = (c => c.Name);
break;
default:
break;
}
if (isAscending)
{
query.OrderBy(param);
}
else
{
query.OrderByDescending(param);
}
grid.DataSource = query.ToList();
grid.DataBind();
Could anyone please help me?
Thanks!
Your code is broken at the moment - you're calling OrderBy/OrderByDescending but not using the result. You need
query = query.OrderBy(param);
etc.
As for conditionally ordering - the problem is that you can't declare the type of param because the type of the ordering key may well vary depending on the sort expression.
As a workaround, you can write your own extension method:
public static IOrderedQueryable<TSource> OrderByWithDirection<TSource, TKey>
(this IQueryable<TSource> source,
Expression<Func<TSource, TKey>> keySelector,
bool ascending)
{
return ascending ? source.OrderBy(keySelector)
: source.OrderByDescending(keySelector);
}
Your code would then become:
IQueryable<User> query = (from c in users select c).AsQueryable<User>();
switch (e.SortExpression)
{
case "Name":
query = query.OrderByWithDirection(c => c.Name, isAscending);
break;
// etc
default:
break;
}
(Why are you calling AsQueryable, by the way?)