Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Collated JSON Tables #420

Open
abhillman opened this issue Jul 29, 2024 · 5 comments
Open

Collated JSON Tables #420

abhillman opened this issue Jul 29, 2024 · 5 comments
Labels
question Further information is requested

Comments

@abhillman
Copy link

use json_to_table::json_to_table;
use serde::Serialize;

#[derive(Serialize)]
struct Thing {
    f1: String,
    f2: String,
    f3: String,
}

fn main() {
    let t1 = Thing {
        f1: "a".to_string(),
        f2: "b".to_string(),
        f3: "c".to_string(),
    };

    let t2 = Thing {
        f1: "d".to_string(),
        f2: "e".to_string(),
        f3: "f".to_string(),
    };

    let t3 = Thing {
        f1: "g".to_string(),
        f2: "h".to_string(),
        f3: "i".to_string(),
    };

    let ts = vec![t1, t2, t3];

    let v = serde_json::to_value(ts).unwrap();
    let jt = json_to_table(&v).into_table();
    let mut tb = tabled::builder::Builder::from(jt);
    let table = tb.index().column(0).transpose().build();

    println!("{}", table.to_string())
}

Results in:

+--------------+--------------+--------------+
| +----+-----+ | +----+-----+ | +----+-----+ |
| | f1 |  a  | | | f1 |  d  | | | f1 |  g  | |
| +----+-----+ | +----+-----+ | +----+-----+ |
| | f2 |  b  | | | f2 |  e  | | | f2 |  h  | |
| +----+-----+ | +----+-----+ | +----+-----+ |
| | f3 |  c  | | | f3 |  f  | | | f3 |  i  | |
| +----+-----+ | +----+-----+ | +----+-----+ |
+--------------+--------------+--------------+

Is there a way to get f1, f2, f3 as the header and values as rows?

@zhiburt
Copy link
Owner

zhiburt commented Jul 29, 2024

Hey @abhillman

So I am sure there are many ways to do that, depending on your needs,
but the easiest based on your example is probably the next one.

use json_to_table::{json_to_table, Orientation};
use serde::Serialize;

#[derive(Serialize)]
struct Thing {
    f1: String,
    f2: String,
    f3: String,
}

fn main() {
    let t1 = Thing {
        f1: "a".to_string(),
        f2: "b".to_string(),
        f3: "c".to_string(),
    };

    let t2 = Thing {
        f1: "d".to_string(),
        f2: "e".to_string(),
        f3: "f".to_string(),
    };

    let t3 = Thing {
        f1: "g".to_string(),
        f2: "h".to_string(),
        f3: "i".to_string(),
    };

    let data = vec![t1, t2, t3];
    let json = serde_json::to_value(data).unwrap();

    let table = json_to_table(&json)
        .object_orientation(Orientation::Row)
        .array_orientation(Orientation::Row)
        .into_table();

    println!("{}", table)
}
+---------------------+---------------------+---------------------+
| +-----+-----+-----+ | +-----+-----+-----+ | +-----+-----+-----+ |
| | f1  | f2  | f3  | | | f1  | f2  | f3  | | | f1  | f2  | f3  | |
| +-----+-----+-----+ | +-----+-----+-----+ | +-----+-----+-----+ |
| |  a  |  b  |  c  | | |  d  |  e  |  f  | | |  g  |  h  |  i  | |
| +-----+-----+-----+ | +-----+-----+-----+ | +-----+-----+-----+ |
+---------------------+---------------------+---------------------+

@zhiburt zhiburt added the question Further information is requested label Jul 29, 2024
@zhiburt
Copy link
Owner

zhiburt commented Jul 29, 2024

Just in case you can customize it for example like this

use json_to_table::{json_to_table, Orientation};
use serde::Serialize;
use tabled::settings::{Padding, Style};

#[derive(Serialize)]
struct Thing {
    f1: String,
    f2: String,
    f3: String,
}

fn main() {
    let t1 = Thing {
        f1: "a".to_string(),
        f2: "b".to_string(),
        f3: "c".to_string(),
    };

    let t2 = Thing {
        f1: "d".to_string(),
        f2: "e".to_string(),
        f3: "f".to_string(),
    };

    let t3 = Thing {
        f1: "g".to_string(),
        f2: "h".to_string(),
        f3: "i".to_string(),
    };

    let data = vec![t1, t2, t3];
    let json = serde_json::to_value(data).unwrap();

    let mut table = json_to_table(&json)
        .object_orientation(Orientation::Row)
        .array_orientation(Orientation::Row)
        .with(Style::modern_rounded())
        .into_table();

    table.with(Style::modern()).with(Padding::zero());

    println!("{}", table)
}
┌───────────────────┬───────────────────┬───────────────────┐
│╭─────┬─────┬─────╮│╭─────┬─────┬─────╮│╭─────┬─────┬─────╮│
││ f1  │ f2  │ f3  │││ f1  │ f2  │ f3  │││ f1  │ f2  │ f3  ││
│├─────┼─────┼─────┤│├─────┼─────┼─────┤│├─────┼─────┼─────┤│
││  a  │  b  │  c  │││  d  │  e  │  f  │││  g  │  h  │  i  ││
│╰─────┴─────┴─────╯│╰─────┴─────┴─────╯│╰─────┴─────┴─────╯│
└───────────────────┴───────────────────┴───────────────────┘

@abhillman
Copy link
Author

Thanks so much for your reply. Is there a minimal way to do something like the following? Of course, I can definitely hack something together... but:

+---------------------+
| +-----+-----+-----+ |
| | f1  | f2  | f3  | |
| +-----+-----+-----+ |
| |  a  |  b  |  c  | |
| |  d  |  e  |  f  | |
| |  g  |  h  |  i  | |
| +-----+-----+-----+ |
+---------------------+

@zhiburt
Copy link
Owner

zhiburt commented Jul 31, 2024

You're right there plenty (and I mean really a lot) of ways to do that.

Somehow I tried to stick to basic tabled without use of json_to_table.

use std::{collections::HashMap, iter::FromIterator};

use tabled::{
    builder::Builder,
    settings::{
        style::{HorizontalLine, On},
        Style,
    },
};

// well... yes a bit complex but it's case we use CONST
const STYLE: Style<On, On, On, On, (), On, 1, 0> = Style::modern()
    .remove_horizontal()
    .horizontals([(1, HorizontalLine::inherit(Style::modern()))]);

fn main() {
    let json = serde_json::json!(
        [
            { "field1": "1" },
            { "field1": "2", "field2": 4 },
            { "field1": "3", "field2": 4 },
            { "field1": "4", "field3": 10 },
        ]
    );

    let map = json
        .as_array()
        .unwrap()
        .iter()
        .map(|value| value.as_object().unwrap())
        .fold(HashMap::new(), |mut acc, row| {
            for (key, element) in row {
                acc.entry(key.to_string())
                    .or_insert_with(Vec::new)
                    .push(element.to_string());
            }

            acc
        });

    let mut builder = Builder::from_iter(map.values());
    builder.insert_column(0, map.keys());

    let builder = builder.index().column(0).transpose();

    let mut table = builder.build();
    table.with(STYLE);

    println!("{}", table);
}
┌────────┬────────┬────────┐
│ field1 │ field2 │ field3 │
├────────┼────────┼────────┤
│ "1"    │ 4      │ 10     │
│ "2"    │ 4      │        │
│ "3"    │        │        │
│ "4"    │        │        │
└────────┴────────┴────────┘

But maybe it's common behaivour (I mean your json example and it worth to add some function to json_to_table to parse it right away. )

@zhiburt
Copy link
Owner

zhiburt commented Jul 31, 2024

My example could be done pretty affective if switched to iterative approach rather then collecting a HashMap.
You could build Builder step by step.

Also as a different approach there could be usage for PoolTable and TableValue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants