【问题标题】:Generate data into recharts bar chart using List of DTO使用 DTO 列表将数据生成到 recharts 条形图中
【发布时间】:2021-11-21 14:59:44
【问题描述】:

我有这个使用 Typescript 从 Rest API 生成的 DTO 对象:

export interface BillingSummaryDTO {
    paid?: number,
    outstanding?: number,
    pastDue?: number,
    cancelled?: number,
    createdAt?: Moment | null,
}

export async function getBillingSummary(): Promise<AxiosResponse<BillingSummaryDTO[]>> {
  return await axios.get<BillingSummaryDTO[]>(
      `${baseUrl}/management/billing/summary`
  );
}

示例图表:

import {
  BarChart,
  Bar,
  XAxis,
  YAxis,
  CartesianGrid,
  Tooltip,
  Legend,
} from "recharts";
import {Box} from "@material-ui/core";

const data = [
  {
    name: "Jan",
    Chargebacks: 4000,
    Transactions: 2400,
    USD: 2400,
  },
  {
    name: "Feb",
    Chargebacks: 3000,
    Transactions: 1398,
    USD: 2210,
  },
  {
    name: "Mar",
    Chargebacks: 2000,
    Transactions: 9800,
    USD: 2290,
  },
  {
    name: "Apr",
    Chargebacks: 2780,
    Transactions: 3908,
    USD: 2000,
  },
  {
    name: "May",
    Chargebacks: 1890,
    Transactions: 4800,
    USD: 2181,
  },
  {
    name: "Jun",
    Chargebacks: 2390,
    Transactions: 3800,
    USD: 2500,
  },
  {
    name: "Jul",
    Chargebacks: 3490,
    Transactions: 4300,
    USD: 2100,
  },
  {
    name: "Aug",
    Chargebacks: 3490,
    Transactions: 4300,
    USD: 2100,
  },
  {
    name: "Sep",
    Chargebacks: 3490,
    Transactions: 4300,
    USD: 2100,
  },
];

const useStyles = makeStyles((theme) =>
  createStyles({
    root: {
      flexGrow: 1,
    },
    paper: {
      padding: theme.spacing(2),
      textAlign: "center",
      color: theme.palette.text.secondary,
    },
  })
);

const usePaperStyles = makeStyles((theme) =>
  createStyles({
    root: {
      display: "flex",
      flexWrap: "wrap",
      "& > *": {
        margin: theme.spacing(1),
        width: theme.spacing(16),
        height: theme.spacing(16),
      },
    },
  })
);

const useTimelineStyles = makeStyles((theme) => ({
  paper: {
    padding: "6px 16px",
  },
  secondaryTail: {
    backgroundColor: theme.palette.secondary.main,
  },
}));

export default function Billing() {
  const [click, setClick] = useState(false);
  const closeMobileMenu = () => setClick(false);
  const classes = useStyles();
  const classesPaper = usePaperStyles();
  const classesTimeline = useTimelineStyles();

  return (
    <>
      <Grid container justifyContent="center" alignItems="center">
        <Grid item xs={11}>
          {/*Padding on the top*/}
          <Box m="5rem" />
        </Grid>

        <Grid item xs={12} >
          <h4 className="chart-label">Invoices Summary</h4>
          <BarChart
            width={1000}
            height={300}
            data={data}
            margin={{
              top: 5,
              right: 30,
              left: 20,
              bottom: 5,
            }}
            barSize={30}
          >
            <XAxis
              dataKey="name"
              scale="point"
              padding={{ left: 10, right: 10 }}
            />
            <YAxis />
            <Tooltip />
            <Legend />
            <CartesianGrid strokeDasharray="3 3" />
            <Bar
              dataKey="Transactions"
              fill="#8884d8"
              background={{ fill: "#eee" }}
            />
          </BarChart>
        </Grid>
      </Grid>
    </>
  );
}

如何使用来自BillingSummaryDTO[] 的数据来生成图表?

【问题讨论】:

    标签: reactjs typescript react-typescript recharts


    【解决方案1】:

    创建一个映射 BillingSummaryDTO[] 的函数并将其转换为您指定供 &lt;BarChart/&gt; 使用的数据模型(在变量 data 中可见)是一种有效的方法。

    export interface BarChartDataModel {
      name: string,
      Chargebacks: number,
      Transactions: number,
      USD: number,
    }
    
    const data : BarChartDataModel []
    

    此数据将在&lt;BarChart/&gt;中使用

    
    <BarChart
                data={data}
                // ....
              >
             // ...
     </BarChart>
    

    这样的函数将遵循 map reduce 方法,并在 momentjs 等 JS 日期管理库的帮助下实现

    四步解决方案

    1. 按时间顺序排列所有账单(最旧的账单在前),以便以后更轻松地进行 map-reduce
    2. 按年将账单分入称为“yearwise-buckets”的存储桶中
    3. 将每个按年存储桶中的账单分桶到称为“按月存储桶”的存储桶中
    4. 将每个月存储桶中的所有账单减少到遵循BarChartDataModel 接口的对象。 BarChartDataModel 接口之后的对象集合是我们传递给&lt;BarChart/&gt; 组件的data
    5. 在我们的&lt;BarChart/&gt; 组件中使用这个对象集合

    你的代码是:

    export interface BillingSummaryDTO {
        paid?: number,
        outstanding?: number,
        pastDue?: number,
        cancelled?: number,
        createdAt?: Moment | null,
    }
    
    export interface BarChartDataModel {
      name: string,
      Chargebacks: number,
      Transactions: number,
      USD: number,
    }
    
    export async function getBillingSummary(): Promise<AxiosResponse<BillingSummaryDTO[]>> {
      const response = await axios.get<BillingSummaryDTO[]>(
          `${baseUrl}/management/billing/summary`
      );
      // STEP 1 : Chronological ordering. Oldest bills will show first
      const chronologicallyOrdered = response.sort((a:BillingSummaryDTO,b:BillingSummaryDTO)=> a.createdAt - b.createdAt )
    
      // STEP 2 : Bucket by year
      const groupByYear = chronologicallyOrdered.reduce((yearwiseBills : any, bill:BillingSummaryDTO, currIdx:number)) => 
         {
            const year = moment(bill.createdAt).year().toString()
            if(!yearwiseBills[year]){ 
              yearwiseBills[year] = []
            } 
            yearwiseBills[year].push(bill)
            return yearwiseBills
         }
      ,{})
    
      // STEP 3 : In each yearwise bucket -> bucket by month
      const groupByMonth = Object.keys(groupByYear).map((year, yearwiseBills) => yearwiseBills.reduce((monthwiseBills: any, bill:BillingSummaryDTO, currIdx:number)) => 
         {
            const moment = moment(bill.createdAt).month().toString()
            if(!yearAcc[month]){ 
              monthwiseBills[month] = []
            } 
            monthwiseBills[month].push(bill)
            return monthwiseBills
         }
      ,{}) );
    
     // STEP 4 : Reduce all bills in a monthwise bucket into a monthlyReport object and store all monthlyReport objects in an monthlyReportArray
     const monthlyReportArray:BarChartDataModel[] = Object.keys(groupByMonth).map((year, yearwiseBills) => 
       Object.keys(bills).map((month, monthwiseBills) => monthwiseBills.reduce((monthlyReport:BarChartDataModel,bill:BillingSummaryDTO) => {
        if(bill.cancelled){
          monthlyReport.Chargebacks++
        }else{
          monthlyReport.Transactions++,
          monthlyReport.USD += bill.paid
        }
        return monthlyReport
      },{
        name : moment(month, 'M').format('MMM')
        Transactions : 0,
        USD : 0,
        Chargebacks:0
       } )
     )
    
    
     // STEP 5 : Consume this as the "data" for the "<BarChart/>" component
     return monthlyReportArray
    }
     
    
    

    我先按年分桶,然后按月分桶,而不是直接按月分桶,因为我们不想将“1997 年 5 月”和“1998 年 5 月”的月度报告合并为“5 月”。图表。我们希望它们分开

    【讨论】:

    • 我收到多个错误。您确定这应该是正确的解决方案吗?
    • 你能分享你的错误吗? map-reduce 的方法是完美的,所以逻辑成立。可能会出现语法问题(主要是类型检查错误)。
    猜你喜欢
    • 1970-01-01
    • 2021-08-13
    • 2013-09-30
    • 1970-01-01
    • 2021-12-09
    • 2020-08-02
    • 1970-01-01
    • 2022-10-05
    • 1970-01-01
    相关资源
    最近更新 更多