webentwicklung-frage-antwort-db.com.de

Elemente nach Datum gruppieren

Ich habe Elemente in meiner Liste und die Elemente haben ein Feld, das das Erstellungsdatum des Elements anzeigt.

 enter image description here

und ich muss sie basierend auf einer "Komprimierung" gruppieren, die der Benutzer gibt. Die Optionen sind Day, Week, Month und Year.

Wenn der Benutzer day-Komprimierung auswählt, muss ich meine Elemente so gruppieren, dass die Elemente, die am selben Tag erstellt werden, gruppiert werden. In meinem obigen Beispiel werden nur Artikel 1 und Artikel 2 am selben Tag erstellt. Die anderen sind ebenfalls Gruppen, aber sie haben nur ein Element, da an ihrem Tag nur ein Element erstellt wird.

{{item1, item2}, {item3}, {item4}, {item5}, {item6}, {item7}}

Wenn der Benutzer week auswählt:

{{item1, item2, item3, item4}, {item5}, {item6}, {item7}}

Wenn der Benutzer month auswählt:

{{item1, item2, item3, item4, item5}, {item6}, {item7}}

Wenn der Benutzer year auswählt:

{{item1, item2, item3, item4, item5, item6}, {item7}}

Nachdem Gruppen erstellt wurden, ist das Datum der Elemente nicht wichtig. Ich meine, der Schlüssel kann alles sein, solange die Gruppen erstellt werden.

Im Falle der Verwendung von Map dachte ich als Schlüssel wie folgt:

day = Tag des Jahres
week = Woche des Jahres
month = Monat des Jahres
year = Jahr

Was wäre die beste Lösung für dieses Problem? Ich konnte nicht mal damit anfangen und mir fällt keine andere Lösung als die Iteration ein. 

7
drJava

Ich würde Collectors.groupingBy mit einer angepassten LocalDate im Klassifizierer verwenden, so dass Elemente mit ähnlichen Datumsangaben (gemäß der vom Benutzer angegebenen compression) gruppiert werden.

Legen Sie dazu zunächst folgende Map an:

static final Map<String, TemporalAdjuster> ADJUSTERS = new HashMap<>();

ADJUSTERS.put("day", TemporalAdjusters.ofDateAdjuster(d -> d)); // identity
ADJUSTERS.put("week", TemporalAdjusters.previousOrSame(DayOfWeek.of(1)));
ADJUSTERS.put("month", TemporalAdjusters.firstDayOfMonth());
ADJUSTERS.put("year", TemporalAdjusters.firstDayOfYear());

Hinweis: Für "day" wird eine TemporalAdjuster verwendet, die das unberührte Datum zulässt.

Als Nächstes verwenden Sie die vom Benutzer angegebene compression, um dynamisch auszuwählen, wie Ihre Liste mit Elementen gruppiert werden soll:

Map<LocalDate, List<Item>> result = list.stream()
    .collect(Collectors.groupingBy(item -> item.getCreationDate()
            .with(ADJUSTERS.get(compression))));

Die LocalDate wird mit der LocalDate.with(TemporalAdjuster) Methode angepasst.

Sie können das von Ihnen beschriebene Verhalten mit Java 8-Streams erhalten:

Map<LocalDate, List<Data>> byYear = data.stream()
        .collect(groupingBy(d -> d.getDate().withMonth(1).withDayOfMonth(1)));
Map<LocalDate, List<Data>> byMonth = data.stream()
        .collect(groupingBy(d -> d.getDate().withDayOfMonth(1)));
Map<LocalDate, List<Data>> byWeek = data.stream()
        .collect(groupingBy(d -> d.getDate().with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY))));
Map<LocalDate, List<Data>> byDay = data.stream()
        .collect(groupingBy(d -> d.getDate()));

Dokumente für groupingBy und collect . In allen 4 Fällen wird LocalDate als Schlüssel verwendet. Um entsprechend zu gruppieren, wird es so geändert, dass alle Datumsangaben denselben Monat und Tag oder denselben Tag haben, jedoch unterschiedliche Monate oder denselben Monat und denselben Wochentag (Montag), was zu offensichtlichen Gruppierungsregeln führt. Das Datum in Ihren Daten ändert nicht nur den Schlüssel. Dies berücksichtigt, dass der Monat derselbe ist, wenn auch das Jahr gleich ist und der Tag derselbe ist, wenn das vollständige Datum gleich ist.

Wenn Sie beispielsweise nach Monat gruppieren, haben diese Daten denselben Schlüssel:

01.01.2017 -> Schlüssel 01.01.2017
01.04.2017 -> Schlüssel 01.01.2017
01.05.2017 -> Schlüssel 01.01.2017 

bei der Gruppierung nach Wochen haben diese Daten den gleichen Schlüssel (Datum ist am vorigen Montag):

01.04.2017 -> Schlüssel 02.01.2017
01.05.2017 -> Schlüssel 02.01.2017 

Sie möchten stattdessen nach demselben Tag des Monats gruppieren, beispielsweise unabhängig von Jahr und Monat. Sie würden es so erreichen:

Map<Integer, List<Data>> byDayOfMonth = data.stream()
        .collect(groupingBy(d -> d.getDate().getDayOfMonth()));

Welche würden 01/01/2017 mit 01/10/2017 und dann 05/01/2017 mit 05/04/2018 zusammenfassen

2

Das einzige Detail, dem ich mehr Aufmerksamkeit schenken würde, ist die Definition der Woche. Ich gehe davon aus, dass Ihre Woche am Sonntag beginnt und wenn mindestens ein Tag für die erste Woche erforderlich ist. (ISO 8601 besagt, dass eine Woche am Montag beginnt und mindestens vier Tage dauern muss, um als erste Woche betrachtet zu werden - wenn es weniger Tage hat, gilt dies als Nullwoche). Sie können im javadoc nach weiteren Details zu den Wochendefinitionen suchen.

Um diese Wochendefinition zu erhalten, verwende ich die Klasse Java.time.temporal.WeekFields. Ich verwende die of-Methode, die explizit den ersten Tag der Woche und die minimale Anzahl von Tagen in der ersten Woche verwendet (wenn ich die Version mit einer Locale benutze, können sich die Ergebnisse je nach Standardgebietsschema des Systems unterscheiden).

Ich habe auch ein Enum für den Komprimierungstyp erstellt, das ist jedoch optional:

enum CompressionType {
    DAY, WEEK, MONTH, YEAR;
}

Wie auch immer, ich verwende den Komprimierungstyp, um zu wissen, in welchem ​​Feld die Daten gruppiert werden. Dann habe ich Collectors.groupingBy verwendet, um die Gruppierung durchzuführen:

// assuming you have a list of dates
List<LocalDate> dates = new ArrayList<>();
dates.add(LocalDate.of(2017, 1, 1));
dates.add(LocalDate.of(2017, 1, 1));
dates.add(LocalDate.of(2017, 1, 4));
dates.add(LocalDate.of(2017, 1, 5));
dates.add(LocalDate.of(2017, 1, 29));
dates.add(LocalDate.of(2017, 10, 1));
dates.add(LocalDate.of(2018, 4, 5));

CompressionType compression = // get CompressionType from user input
final TemporalField groupField;
switch (compression) {
    case DAY:
        groupField = ChronoField.DAY_OF_YEAR;
        break;
    case WEEK:
    // week starts at Sunday, minimum of 1 day in the first week
        groupField = WeekFields.of(DayOfWeek.SUNDAY, 1).weekOfWeekBasedYear();
        break;
    case MONTH:
        groupField = ChronoField.MONTH_OF_YEAR;
        break;
    case YEAR:
        groupField = ChronoField.YEAR;
        break;
    default:
        groupField = null;
}
if (groupField != null) {
    Map<Integer, List<LocalDate>> result = dates.stream().collect(Collectors.groupingBy(d -> d.get(groupField)));
}
2
user7605325

Verwenden Sie eine HashMap wie diese

 <Integer,ArrayList<Date>>

1. set filter=DAY/MONTH/YEAR

2.Iterate the date_obj

3.Use Calendar package to get a variable val=Calendar.get(filter)

4. If hashmap.keyset().contains(val)
      hashmap.get(val).add(date_obj.date)
   Else
      hashmap.put(val,new ArrayList<date_obj>());
1
Joey Pinto

Angenommen, Ihr Elementelement hat die folgenden Attribute: 

private String item;
private LocalDate date;

Das kannst du so machen: 

ArrayList<Element> list = new ArrayList<>();
list.add(new Element("item 1", LocalDate.of(2017, 01, 01)));
list.add(new Element("item 2", LocalDate.of(2017, 01, 01)));

WeekFields weekFields = WeekFields.of(Locale.getDefault());
String userChoice = new Scanner(System.in).nextLine();
Map<Integer, List<Element>> map;

switch (userChoice) {
     case "day":
          map = list.stream().collect(Collectors.groupingBy(element 
                              -> element.getDate().getDayOfMonth()));
          break;
     case "week":
          map = list.stream().collect(Collectors.groupingBy(element 
                              -> element.getDate().get(weekFields.weekOfWeekBasedYear())));
          break;
     case "month":
          map = list.stream().collect(Collectors.groupingBy(element 
                              -> element.getDate().getMonthValue()));
          break;
     case "year":
          map = list.stream().collect(Collectors.groupingBy(element 
                              -> element.getDate().getYear()));
          break;
     default:
          break;
}

Abhängig von der Benutzerauswahl würde die Karte dazu führen, dass die Elemente nach seiner Auswahl abgebildet werden


Einzelheiten :  

map = list.stream().collect(Collectors.groupingBy(element 
                                        -> element.getDate().getYear()));

Dadurch wird das Element der Liste durchlaufen und der year of the date of the item angezeigt, um sie danach zu gruppieren

1
azro