I have an ArrayList where I add customers. What i wnat to do is that i want to sort them, so they appear sorted on Console.
private static ArrayList <Kund> klista = new ArrayList<>();
Kund kundd = new Kund("a","b");
System.out.print("Namn: ");
String namn = scr.next();
System.out.print("Adress: ");
String adress = scr.next();
if (!namnKontroll(namn)){
System.out.println (namn + " " +"har lagts till \n");
klista.add(kundd);
Kund k = new Kund(namn, adress);
klista.add(k);
}else{
System.out.println("Kunden med det namnet finns redan i systemet!");
}
// this is how i add customers to my ArrayList. So now, how it is possible to sort those names in ArrayList. I want to sort them with Collections. thanks
Try use Collections.sort(klista, theComparator). You will need create a Comparator, like this:
public class KundComparator implements Comparator<Kund> {
#Override
public int compare(Kund o1, Kund o2) {
// write comparison logic here
return o1.getID().compareTo(o2.getID());
}
}
Then use the Comparator:
Collections.sort(klista, new KundComparator());
If you are using Java 8, you can do like this:
Collections.sort(klista, (Kund k1, Kund k2) -> k1.getId().compareTo(k2.getId()));
Related
I saw examples where I can sort a list in dart using one property in flutter(dart).
But how can I do the functionality which an SQL query does like for example:
order by points desc, time asc
You can sort the list then sort it again..
Here is a sample I made from dartpad.dev
void main() {
Object x = Object(name: 'Helloabc', i: 1);
Object y = Object(name: 'Othello', i: 3);
Object z = Object(name: 'Avatar', i: 2);
List<Object> _objects = [
x, y, z
];
_objects.sort((a, b) => a.name.length.compareTo(b.name.length));
/// second sorting
// _objects.sort((a, b) => a.i.compareTo(b.i));
for (Object a in _objects) {
print(a.name);
}
}
class Object {
final String name;
final int i;
Object({this.name, this.i});
}
I was able to find an answer for this. Thanks to #pskink and the url
https://www.woolha.com/tutorials/dart-sorting-list-with-comparator-and-comparable.
I implemented the Comparable to sort by the two properties.
class Sample implements Comparable<Sample> {
final int points;
final int timeInSeconds;
Sample(
{
this.points,
this.timeInSeconds});
#override
int compareTo(Sample other) {
int pointDifference = points- other.points;
return pointDifference != 0
? pointDifference
: other.timeInSeconds.compareTo(this.timeInSeconds);
}
}
sampleList.sort();
I have class Element with a list, my intended output is like this:
Map<String , List<Element>>
{
1 = [Element3, Element1],
2 = [Element2, Element1],
3 = [Element2, Element1], 4=[Element2]
}
And my input is set of element objects, I used forEach to get the desired outcome, but I'm looking for how to collect it using collectors.toMap. Any inputs are much appreciated
Set<Element> changes = new HashSet();
List<String> interesetList = new ArrayList();
interesetList.add("1");
interesetList.add("2");
interesetList.add("3");
Element element = new Element(interesetList);
changes.add(element);
interesetList = new ArrayList();
interesetList.add("2");
interesetList.add("3");
interesetList.add("4");
element = new Element(interesetList);
changes.add(element);
Map<String, List<Element>> collect2 = new HashMap();
changes.forEach(element -> {
element.getInterestedList().forEach(tracker -> {
collect2.compute(tracker, ( key , val) -> {
List<Element> elementList = val == null ? new ArrayList<Element>() : val;
elementList.add(Element);
return elementList;
});
});
});
class Element {
List<String> interestedList;
static AtomicInteger sequencer = new AtomicInteger(0);
String mName;
public Element(List<String> aList) {
interestedList = aList;
mName = "Element" + sequencer.incrementAndGet();
}
public List<String> getInterestedList() {
return interestedList;
}
#Override
public String toString() {
return mName;
}
}
You can do it by using Collectors.groupingBy instead of Collectors.toMap, along with Collectors.mapping, which adapts a collector to another collector:
Map<String, List<Element>> result = changes.stream()
.flatMap(e -> e.getInterestedList().stream().map(t -> Map.entry(t, e)))
.collect(Collectors.groupingBy(
Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
You need to use the Stream.flatMap method first and then pair the elements of the inner lists with the current Element instance. I did this via the new Java 9's Map.entry(key, value) method. If you're not on Java 9 yet, you could change it to new AbstractMap.SimpleEntry<>(key, value).
After flatmapping, we need to collect instances of Map.Entry. So I'm using Collectors.groupingBy to classify entries by key (where we had previously stored each element of the inner lists, aka what you call tracker in your code). Then, as we don't want to have instances of List<Map.Entry<String, Element>> as the values of the map, we need to transform each Map.Entry<String, Element> of the stream to just Element (that's why I'm using Map.Entry::getValue as the first argument of Collectors.mapping). We also need to specify a downstream collector (here Collectors.toList()), so that the outer Collectors.groupingBy collector knows where to place all the adapted elements of the stream that belong to each group.
A shorter and surely more efficient way to do the same (similar to your attempt) could be:
Map<String, List<Element>> result = new HashMap<>();
changes.forEach(e ->
e.getInterestedList().forEach(t ->
result.computeIfAbsent(t, k -> new ArrayList<>()).add(e)));
This uses Map.computeIfAbsent, which is a perfect fit for your use case.
there's one Object
#Data
class ScoreInfo{
String id;
float cove_score;
float theam_score;
float content_score;
float teach_score;
Date create_date;
ScoreInfoP scoreInfoP;
}
and ScoreInfoP is :
#Data
class ScoreInfoP{
String stream_sn;
String anchor_id;
String create_by;
}
sourceList is a list of ScoreInfo,I want to get cove_score,theam_score,content_score,teach_score's mean values,group by scoreInfoP property and return four mean values for each of these properties.
I can get only one mean value using such code:
Map<ScoreInfoP, Double> meanForCoveScore = sourceList.stream().collect(Collectors.groupingBy(ScoreInfo::getScoreInfoP,
Collectors.averagingDouble(ScoreInfo::getCove_score)));
I want to learn how to get four mean values using java8 or any easier way you suggest achieving this.
Waiting here for your generous help.
There isn't anything build-in for this, but it's not that complicated to build a custom Collector for that...
Map<String, List<Float>> result = Arrays.asList(first, second)
.stream()
.collect(Collectors.groupingBy(
x -> x.getScoreInfoP().getAnchorId(),
Collector.of(
() -> new float[5],
(a, x) -> {
a[0] += x.getCoveScore();
a[1] += x.getTheamScore();
a[2] += x.getTeachScore();
a[3] += x.getContentScore();
a[4]++;
},
(left, right) -> {
for (int i = 0; i < 4; ++i) {
left[i] += right[i];
}
return left;
}, x -> Arrays.asList(x[0] / x[4], x[1] / x[4], x[2] / x[4], x[3] / x[4]))
));
System.out.println(result);
I actually groupBy here on ScoreInfoP#anchorId; but you can do it on ScoreInfoP - for that you need to change x -> x.getScoreInfoP().getAnchorId() to x -> x.getScoreInfoP(). But obviously ScoreInfoP needs to override hashCode and equals.
As I said in the comment you should use a proper result class.
class ScoreInfoAverage {
private float cove_score;
private float theam_score;
private float content_score;
private float teach_score;
// ctor, getter, setter
}
Then you can use a custom Collector:
public static Collector<ScoreInfo, ?, ScoreInfoAverage> scoreInfoToAverage() {
class ScoreInfoAccumulator {
private DoubleSummaryStatistics cove_score = new DoubleSummaryStatistics();
private DoubleSummaryStatistics theam_score = new DoubleSummaryStatistics();
private DoubleSummaryStatistics content_score = new DoubleSummaryStatistics();
private DoubleSummaryStatistics teach_score = new DoubleSummaryStatistics();
public void add(ScoreInfo si) {
cove_score.accept(si.cove_score);
theam_score.accept(si.theam_score);
content_score.accept(si.content_score);
teach_score.accept(si.teach_score);
}
public ScoreInfoAccumulator combine(ScoreInfoAccumulator sia) {
cove_score.combine(sia.cove_score);
theam_score.combine(sia.theam_score);
content_score.combine(sia.content_score);
teach_score.combine(sia.teach_score);
return this;
}
public ScoreInfoAverage average() {
return new ScoreInfoAverage((float) cove_score.getAverage(),
(float) theam_score.getAverage(), (float) content_score.getAverage(),
(float) teach_score.getAverage());
}
}
return Collector.of(ScoreInfoAccumulator::new, ScoreInfoAccumulator::add,
ScoreInfoAccumulator::combine, ScoreInfoAccumulator::average);
}
Last but not least you add your Collector to the downstream:
Map<ScoreInfoP, ScoreInfoAverage> collect = scoreInfos.stream()
.collect(Collectors.groupingBy(ScoreInfo::getScoreInfoP, scoreInfoToAverage()));
My reducer gives this o/p
Country-Year,Medals
India-2008,60
United States-2008,1237
Zimbabwe-2008, 2
Namibia-2009,22
China-2009,43
United States-2009,54
And i want this, sorting should happen based on medals and top three should be shown.
Country-Year,Medals
United States-2008,1237
India-2008,60
United States-2009,54
Someone suggested me to do this sorting in custom recordreader(understood that it is used in mapper part) and i browsed through some resources but couldn't find much on sorting. Please share any ideas or link to resources. Advance thanks !
You can implement Map Reduce Top K Design pattern to achieve your objective.
Top K Design pattern will sort your records on values and picks the top k records.
You can go through this link for implementing Top K Design pattern on your data.
When you aggregate the result of mapper in Reducer class rather than writing it to output take it into a map then sort the map and display result accordingly.
Key = Country-Year , Value = Medals
Dummy code to showcase how to implement
public class Medal_reducer extends Reducer<Text,IntWritable, Text , IntWritable> {
// Change access modifier as per your need
public Map<String , Integer > map = new HashMap<String , Integer>();
public void reduce(Text key , Iterable<IntWritable> values ,Context context )
{
// write logic for your reducer
// Enter reduced values in map for each key
for (IntWritable value : values ){
// calculate count
}
map.put(key.toString() , count);
}
public void cleanup(Context context){
//Cleanup is called once at the end to finish off anything for reducer
//Here we will write our final output
Map<String , Integer> sortedMap = new HashMap<String , Integer>();
sortedMap = sortMap(map);
for (Map.Entry<String,Integer> entry = sortedMap.entrySet()){
context.write(new Text(entry.getKey()),new IntWritable(entry.getValue()));
}
}
public Map<String , Integer > sortMap (Map<String,Integer> unsortMap){
Map<String ,Integer> hashmap = new HashMap<String,Integer>();
int count=0;
List<Map.Entry<String,Integer>> list = new LinkedList<Map.Entry<String,Integer>>(unsortMap.entrySet());
//Sorting the list we created from unsorted Map
Collections.sort(list , new Comparator<Map.Entry<String,Integer>>(){
public int compare (Map.Entry<String , Integer> o1 , Map.Entry<String , Integer> o2 ){
//sorting in descending order
return o2.getValue().compareTo(o1.getValue());
}
});
for(Map.Entry<String, Integer> entry : list){
// only writing top 3 in the sorted map
if(count>2)
break;
hashmap.put(entry.getKey(),entry.getValue());
}
return hashmap ;
}
}
Hopefully this will help.
Initial data:
public class Stats {
int passesNumber;
int tacklesNumber;
public Stats(int passesNumber, int tacklesNumber) {
this.passesNumber = passesNumber;
this.tacklesNumber = tacklesNumber;
}
public int getPassesNumber() {
return passesNumber;
}
public void setPassesNumber(int passesNumber) {
this.passesNumber = passesNumber;
}
public int getTacklesNumber() {
return tacklesNumber;
}
public void setTacklesNumber(int tacklesNumber) {
this.tacklesNumber = tacklesNumber;
}
}
Map<String, List<Stats>> statsByPosition = new HashMap<>();
statsByPosition.put("Defender", Arrays.asList(new Stats(10, 50), new Stats(15, 60), new Stats(12, 100)));
statsByPosition.put("Attacker", Arrays.asList(new Stats(80, 5), new Stats(90, 10)));
I need to calculate an average of Stats by position. So result should be a map with the same keys, however values should be aggregated to single Stats object (List should be reduced to single Stats object)
{
"Defender" => Stats((10 + 15 + 12) / 3, (50 + 60 + 100) / 3),
"Attacker" => Stats((80 + 90) / 2, (5 + 10) / 2)
}
I don't think there's anything new in Java8 that could really help in solving this problem, at least not efficiently.
If you look carefully at all new APIs, then you will see that majority of them are aimed at providing more powerful primitives for working on single values and their sequences - that is, on sequences of double, int, ? extends Object, etc.
For example, to compute an average on sequence on double, JDK introduces a new class - DoubleSummaryStatistics which does an obvious thing - collects a summary over arbitrary sequence of double values.
I would actually suggest that you yourself go for similar approach: make your own StatsSummary class that would look along the lines of this:
// assuming this is what your Stats class look like:
class Stats {
public final double a ,b; //the two stats
public Stats(double a, double b) {
this.a = a; this.b = b;
}
}
// summary will go along the lines of:
class StatsSummary implements Consumer<Stats> {
DoubleSummaryStatistics a, b; // summary of stats collected so far
StatsSummary() {
a = new DoubleSummaryStatistics();
b = new DoubleSummaryStatistics();
}
// this is how we collect it:
#Override public void accept(Stats stat) {
a.accept(stat.a); b.accept(stat.b);
}
public void combine(StatsSummary other) {
a.combine(other.a); b.combine(other.b);
}
// now for actual methods that return stuff. I will implement only average and min
// but rest of them are not hard
public Stats average() {
return new Stats(a.getAverage(), b.getAverage());
}
public Stats min() {
return new Stats(a.getMin(), b.getMin());
}
}
Now, above implementation will actually allow you to express your proper intents when using Streams and such: by building a rigid API and using classes available in JDK as building blocks, you get less errors overall.
However, if you only want to compute average once somewhere and don't need anything else, coding this class is a little overkill, and here's a quick-and-dirty solution:
Map<String, Stats> computeAverage(Map<String, List<Stats>> statsByPosition) {
Map<String, Stats> averaged = new HashMap<>();
statsByPosition.forEach((position, statsList) -> {
averaged.put(position, averageStats(statsList));
});
return averaged;
}
Stats averageStats(Collection<Stats> stats) {
double a, b;
int len = stats.size();
for(Stats stat : stats) {
a += stat.a;
b += stat.b;
}
return len == 0d? new Stats(0,0) : new Stats(a/len, b/len);
}
There is probably a cleaner solution with Java 8, but this works well and isn't too complex:
Map<String, Stats> newMap = new HashMap<>();
statsByPosition.forEach((key, statsList) -> {
newMap.put(key, new Stats(
(int) statsList.stream().mapToInt(Stats::getPassesNumber).average().orElse(0),
(int) statsList.stream().mapToInt(Stats::getTacklesNumber).average().orElse(0))
);
});
The functional forEach method lets you iterate over every key value pair of your given map.
You just put a new entry in your map for the averaged values. There you take the key you have already in your given map. The new value is a new Stats, where the arguments for the constructor are calculated directly.
Just take the value of your old map, which is the statsList in the forEach function, map the values from the given stats to Integer value with mapToInt and use the average function.
This function returns an OptionalDouble which is nearly the same as Optional<Double>. Preventing that anything didn't work, you use its orElse() method and pass a default value (like 0). Since the average values are double you have to cast the value to int.
As mentioned, there doubld probably be a even shorter version, using reduce.
You might as well use custom collector. Let's add the following methods to Stats class:
public Stats() {
}
public void accumulate(Stats stats) {
passesNumber += stats.passesNumber;
tacklesNumber += stats.tacklesNumber;
}
public Stats combine(Stats acc) {
passesNumber += acc.passesNumber;
tacklesNumber += acc.tacklesNumber;
return this;
}
#Override
public String toString() {
return "Stats{" +
"passesNumber=" + passesNumber +
", tacklesNumber=" + tacklesNumber +
'}';
}
Now we can use Stats in collect method:
System.out.println(statsByPosition.entrySet().stream().collect(
Collectors.toMap(
entity -> entity.getKey(),
entity -> {
Stats entryStats = entity.getValue().stream().collect(
Collector.of(Stats::new, Stats::accumulate, Stats::combine)
); // get stats for each map key.
// get average
entryStats.setPassesNumber(entryStats.getPassesNumber() / entity.getValue().size());
// get average
entryStats.setTacklesNumber(entryStats.getTacklesNumber() / entity.getValue().size());
return entryStats;
}
))); // {Attacker=Stats{passesNumber=85, tacklesNumber=7}, Defender=Stats{passesNumber=12, tacklesNumber=70}}
If java-9 is available and StreamEx, you could do :
public static Map<String, Stats> third(Map<String, List<Stats>> statsByPosition) {
return statsByPosition.entrySet().stream()
.collect(Collectors.groupingBy(e -> e.getKey(),
Collectors.flatMapping(e -> e.getValue().stream(),
MoreCollectors.pairing(
Collectors.averagingDouble(Stats::getPassesNumber),
Collectors.averagingDouble(Stats::getTacklesNumber),
(a, b) -> new Stats(a, b)))));
}