Pagina 1 di 2 1 2 ultimoultimo
Visualizzazione dei risultati da 1 a 10 su 20
  1. #1

    Raggruppare JSON per chiave con conteggio

    ciao!

    avrei una esigenza sul parsing di un JSON:
    codice:
    [{
        "id": "116",
        "title": "vortice",
        "author": "clive cussler",
        "editor": "tea due",
        "price": "8.00",
        "isbn": "88-7818-498-5",
        "note": ""
    }, {
        "id": "4",
        "title": "walhalla",
        "author": "clive cussler",
        "editor": "tea due",
        "price": "8.50",
        "isbn": "88-502-0499-x",
        "note": ""
    }, {
        "id": "171",
        "title": "web penetration testing with kali lnux",
        "author": "joseph muniz, aamir lakhani",
        "editor": "packt publishing",
        "price": "0.00",
        "isbn": "978-1-78216-316-9",
        "note": "informatica - libro elettronico"
    }, {
        "id": "89",
        "title": "zanna bianca",
        "author": "jack london",
        "editor": "de agostini",
        "price": "0.00",
        "isbn": "88-410-0523-8",
        "note": "prezzo in lire"
    }]
    io averi bisogno di ottenere un set di dati che conti quanti libri ha scritto ogni autore.
    quindi, nel caso in questione:
    codice:
    clive cussle -> 2
    joseph muniz, aamir lakhani -> 1
    jack london -> 1
    come libreria per il parsing sto usando gson.
    avete qualche consiglio??

  2. #2
    Utente di HTML.it L'avatar di andbin
    Registrato dal
    Jan 2006
    residenza
    Italy
    Messaggi
    18,254
    Quote Originariamente inviata da fermat Visualizza il messaggio
    io averi bisogno di ottenere un set di dati che conti quanti libri ha scritto ogni autore.
    quindi, nel caso in questione:

    come libreria per il parsing sto usando gson.
    avete qualche consiglio??
    Beh, Gson non c'entra niente in tal senso, si occupa solo del mapping tra JSON e classi Java, non può fare alcuna logica, ricerca o analisi particolare sul contenuto.

    Avrai sicuramente una classe es. Libro, quindi a fronte di quel JSON dovrai arrivare ad avere un array Libro[] o una lista List<Libro> contenente gli oggetti con quei dati. A quel punto dovrai fare l'analisi che hai descritto con del codice Java. La cosa tipica è sfruttare una "map", in questo caso un Map<String,Integer> dove la chiave è l'author e il valore è un contatore. Scansioni lista/array, per ogni author incrementi il valore nella mappa (o setti 1 se non c'è ancora un valore).

    Se puoi usare Java 8, con le sue belle cose lambda expression e Stream API, lo puoi fare anche molto elegantemente e in modo compatto.
    Andrea, andbin.devSenior Java developerSCJP 5 (91%) • SCWCD 5 (94%)
    Java Versions Cheat Sheet

  3. #3
    ciao andbin!

    si infatti sto cercando di seguire la strada con Java 8.
    però ho qualche problema.
    questa la classe Book:
    codice:
    public class Book { 
     
        private String id; 
        private String title; 
        private String author; 
        private String editor; 
        private String price; 
        private String isbn; 
        private String note; 
             
        // GETTER / SETTER 
    }
    poi:
    codice:
        public void groupJson(File file) throws IOException { 
            Gson gson = new Gson(); 
            Type listType = new TypeToken<ArrayList<Book>>() { 
            }.getType(); 
            try (JsonReader reader = new JsonReader(new FileReader(file))) { 
                List<Book> list = gson.fromJson(reader, listType); 
                Map<String, Integer> map = list.stream(). 
                        collect(Collectors.groupingBy(Book::getAuthor, Collectors.counting())); 
            } 
        }
    così ottengo una lista di Book.
    il mio problema è nella map.
    non riesco ad applicare bene la Map.

  4. #4
    Utente di HTML.it L'avatar di andbin
    Registrato dal
    Jan 2006
    residenza
    Italy
    Messaggi
    18,254
    Quote Originariamente inviata da fermat Visualizza il messaggio
    codice:
                Map<String, Integer> map = list.stream(). 
                        collect(Collectors.groupingBy(Book::getAuthor, Collectors.counting()));
    il mio problema è nella map.
    non riesco ad applicare bene la Map.
    Quasi tutto giusto ... salvo un "piccolo" dettaglio: il tipo del counting.

    Nel framework sfortunatamente non hanno messo un es. intCounting() e longCounting() (non chiedermi perché, non lo so .... io li avrei messi, cioè almeno uno per Integer e uno per Long).
    Hanno messo solo un counting() che ha come tipo del conteggio Long.

    Quindi la variabile map semplicemente devi metterla

    Map<String, Long> map = list.stream()..........

    Se vuoi farti un "counting" su Integer, puoi farlo, è abbastanza banale perché il counting è semplicemente un reducing prefissato in un certo modo.
    Andrea, andbin.devSenior Java developerSCJP 5 (91%) • SCWCD 5 (94%)
    Java Versions Cheat Sheet

  5. #5
    si infatti così funziona:
    codice:
        public <T> Map<String, Long> groupJson(File file, TypeToken<T> typeToken) throws IOException { 
            Map<String, Long> map; 
            try (JsonReader reader = new JsonReader(new FileReader(file))) { 
                List<Book> list = gson.fromJson(reader, typeToken.getType()); 
                map = list.stream().collect(Collectors.groupingBy(Book::getAuthor, Collectors.counting())); 
            } 
            return map; 
        }
    allora, cercando di evolvere ulteriormente il metodo:
    - dovrei raggruppare sia per getAuthor, che per getEditor (in punti diversi del programma), ma non saprei come indicare per cosa ragguppare
    - se volessi generalizzare la lista, sarebbe possibile (List<Book>)? me lo chiedo perchè mi viene in mente che se lo generalizzo per List<T>, ovviamente poi non ha accesso diretto ai metodi getAuthor e getEditor, quindi come si può generalizzare la lista? (penso che questa domanda si indirettamente collegata alla prima....).

  6. #6
    Utente di HTML.it L'avatar di andbin
    Registrato dal
    Jan 2006
    residenza
    Italy
    Messaggi
    18,254
    Quote Originariamente inviata da fermat Visualizza il messaggio
    - dovrei raggruppare sia per getAuthor, che per getEditor (in punti diversi del programma), ma non saprei come indicare per cosa ragguppare
    Quindi author E editor come "chiave composta" per la classificazione applicata dal groupingBy?
    Allora dovresti fare una classe che rappresenta l'insieme di author E editor.

    Quote Originariamente inviata da fermat Visualizza il messaggio
    se volessi generalizzare la lista, sarebbe possibile (List<Book>)? me lo chiedo perchè mi viene in mente che se lo generalizzo per List<T>, ovviamente poi non ha accesso diretto ai metodi getAuthor e getEditor, quindi come si può generalizzare la lista? (penso che questa domanda si indirettamente collegata alla prima....).
    Innanzitutto consiglio: separa la lettura/parsing da JSON da altre logiche di analisi/raggruppamenti.

    Quindi un metodo es.

    public List<Book> readBooksJson(File file) throws IOException

    (il TypeToken lo puoi fare all'interno o tenerlo come campo static final, dato che sostanzialmente è una "costante").

    e poi es.

    public Map<String,Long> countBooksByAuthor(List<Book> booksList)
    Andrea, andbin.devSenior Java developerSCJP 5 (91%) • SCWCD 5 (94%)
    Java Versions Cheat Sheet

  7. #7
    no no, non come chiave composta.
    in sostanza devo creare due grafici:
    - in uno metto il numero di libri scritti da ogni autore
    - nell'altro metto il numero di libri emessi da ogni editore

    quindi quel metodo dovrebbe essere richiamo due volte:
    per autori:
    codice:
    map = list.stream().collect(Collectors.groupingBy(Book::getAuthor, Collectors.counting()));
    e per editori:
    codice:
    map = list.stream().collect(Collectors.groupingBy(Book::getEditor, Collectors.counting()));
    quindi sono due grafici separati!

    ------------------------------------------------------------------------------------------------------

    per il secondo quesito, il TypeToken l'ho messo come parametro perchè glielo passo in questo modo (come mi avevi consigliato in un altro thread):
    codice:
    ReadJson jsonRead = new ReadJson(); 
    TypeToken<List<Book>> bookToken = new TypeToken<List<Book>>() { 
    }; 
    System.out.println(jsonRead.groupJson(new File(UrlAndPath.JSON_LIBRI), bookToken));
    giusto per generalizzare il TypeToke che gli passo.

  8. #8
    Utente di HTML.it L'avatar di andbin
    Registrato dal
    Jan 2006
    residenza
    Italy
    Messaggi
    18,254
    Quote Originariamente inviata da fermat Visualizza il messaggio
    no no, non come chiave composta.
    Ah ... meno male (c'è meno lavoro).

    Quote Originariamente inviata da fermat Visualizza il messaggio
    in sostanza devo creare due grafici:
    - in uno metto il numero di libri scritti da ogni autore
    - nell'altro metto il numero di libri emessi da ogni editore

    quindi quel metodo dovrebbe essere richiamo due volte:
    per autori:
    codice:
    map = list.stream().collect(Collectors.groupingBy(Book::getAuthor, Collectors.counting()));
    e per editori:
    codice:
    map = list.stream().collect(Collectors.groupingBy(Book::getEditor, Collectors.counting()));
    quindi sono due grafici separati!
    Ok, allora puoi fare due metodi distinti es. countBooksByAuthor (come ho indicato prima) e l'altro similare countBooksByEditor.

    Se volessi fare un singolo metodo "parametrizzabile" con il classificatore, sappi che si può anche fare.

    Quote Originariamente inviata da fermat Visualizza il messaggio
    per il secondo quesito, il TypeToken l'ho messo come parametro perchè glielo passo in questo modo (come mi avevi consigliato in un altro thread):
    No ok, nessun problema. Su questo aspetto, di come leggere il JSON si può fare in diversi modi a seconda di quanto/come vuoi generalizzare oppure no. Solo questione di scelte.


    P.S. ho notato ora:

    "price": "8.50"

    occhio ai prezzi trattati come stringhe. Se devi solo stamparli, ok.
    Ultima modifica di andbin; 22-06-2017 a 21:50
    Andrea, andbin.devSenior Java developerSCJP 5 (91%) • SCWCD 5 (94%)
    Java Versions Cheat Sheet

  9. #9
    ok, i metodi li ho messi così:
    codice:
        public <T> List<Book> groupJson(File file, TypeToken<T> typeToken) throws IOException { 
            List<Book> list; 
            try (JsonReader reader = new JsonReader(new FileReader(file))) { 
                list = gson.fromJson(reader, typeToken.getType()); 
            } 
            return list; 
        } 
     
        public Map<String, Long> countByAuthor(List<Book> booksList) { 
            Map<String, Long> map = booksList.stream().collect(Collectors.groupingBy(Book::getAuthor, Collectors.counting())); 
            return map; 
        } 
     
        public Map<String, Long> countByEditor(List<Book> booksList) { 
            Map<String, Long> map = booksList.stream().collect(Collectors.groupingBy(Book::getEditor, Collectors.counting())); 
            return map; 
        }
    in verità più che il prezzo, ho un altro problema con questa Map:
    codice:
    ReadJson jsonRead = new ReadJson(); 
    TypeToken<List<Book>> bookToken = new TypeToken<List<Book>>() { 
    }; 
    List<Book> booksList = jsonRead.groupJson(new File(UrlAndPath.JSON_LIBRI), bookToken); 
    Map<String, Long> map = jsonRead.countByAuthor(booksList); 
     
    Map<String, Long> mapTest = new HashMap<>(); 
    mapTest.put("thomas mann", 1L); 
    mapTest.put("clive cussler", 3L); 
    mapTest.put("stephen king", 6L); 
     
    XYChart.Series<String, Number> series1 = new XYChart.Series<>(); 
    series1.setName(title); 
    for (Map.Entry<String, Long> entry : map.entrySet()) { 
        String tmpString = entry.getKey(); 
        Long tmpValue = entry.getValue(); 
        XYChart.Data<String, Number> d = new XYChart.Data<>(tmpString, tmpValue); 
        System.out.println(d); 
        series1.getData().add(d); 
    } 
    barChart.setTitle(title); 
    barChart.getData().addAll(series1);
    vado a riempire una Barchart iterando sulla map.
    se uso l'oggetto map, vedo gli autori, ma il valore è sempre zero.
    se uso l'oggetto mapTest invece funziona.
    come se il valore del json lo leggesse in maniera non corretta.

    però, se stampo il valore della serie (d) con l'oggetto map, mi esce questo:
    codice:
    Data[thomas mann,1,null] 
    Data[giorgio zarelli,1,null] 
    Data[jack london,1,null]
    quindi in verità sembra corretto!

  10. #10
    no, ok ho capito l'inghippo.
    ci sono troppi valori.
    infatti se li limito a 10 funziona:
    codice:
    int count = 0; 
    for (Map.Entry<String, Long> entry : map.entrySet()) { 
        String tmpString = entry.getKey(); 
        Long tmpValue = entry.getValue(); 
        XYChart.Data<String, Number> d = new XYChart.Data<>(tmpString, tmpValue); 
        //System.out.println(tmpString + ": " + tmpValue); 
        //System.out.println(d); 
        series1.getData().add(d); 
        count++; 
        if (count == 10) { 
            break; 
        } 
    }
    a questo punto, però, mi conviene ordinare la map quando la raggruppo!

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved.