从多个对象中拉出相同的密钥

Pull same key out of multiple objects

我有 JSON 个这样的数据

{
"_id": "621813467b8cfa82889de451",
"name": "Test Student",
"parentEmail": "test@example.com",
"teacher": "Mr Teacher",
"scores": [
    {
    "Raise Hand": "0",
    "Ask For Help": "2",
    "email": "test@example.com",
    "date": "2022-02-24"
    },
    {
    "Raise Hand": "2",
    "Ask For Help": "4",
    "email": "test@example.com",
    "date": "2022-02-24"
    }
],
"goals": [
    {
    "type": "Multi Choice",
    "name": "Raise Hand",
    "_id": "6218140f7b8cfa82889de45f"
    },
    {
    "type": "Multi Choice",
    "name": "Ask For Help",
    "_id": "621814187b8cfa82889de470"
    }
],
"__v": 4
}

我试图让“分数”数据有一个带 chart.js 的条形图(使用日期作为 x 轴,使用分数作为 y 轴),但我不知道如何动态提取数据。

我需要它是动态的,以便用户可以通过 GUI 添加新目标并提交新一天的分数,然后添加新图表或向条形图中添加新条形。

我也在用 svelte。

这是我目前拥有的:

//dummy data to resemble data from database
const students = [
    {
    email: "ads",
        "Raise Hand": 1,
        "Ask For Help": 2,
        date: "2022-2-23",
        comment: "Good",
    },
    {
    email: "ads",
        "Raise Hand": 3,
        "Ask For Help": 4,
        date: "2022-2-24",
        comment: "No Comment",
    },
    {
    email: "ads",
        "Raise Hand": 1,
        "Ask For Help": 2,
        date: "2022-2-25",
        comment: "No Comment",
    },
    {
    email: "ads",
        "Raise Hand": 3,
        "Ask For Help": 4,
        "Do Work": 3,
        date: "2022-2-25",
        comment: "No Comment",
    },
    {
    email: "ads",
        "Raise Hand": 1,
        "Ask For Help": 2,
        "Do Work": 3,
        "New One": 4,
        date: "2022-2-25",
        comment: "No Comment",
    },
];

let data = [];

let newKey;

getKeys();

async function getKeys() {
    for (let i = 0; i < students.length; i++) {
        for (let key in students[i]) {
            if (
        key == "email" ||
                key == "date" ||
                key.toLowerCase().includes("comment")
            ) {
                continue;
            } else {
                newKey = key;
                const newValue = students[i][key];
                const amountOfKeys = Object.keys(students[i]).length;

                await checkKey(key, i)
            }
        }
    }
  filter(data)
}

function checkKey(key, num) {
  let newArr = [];
  let tempObj = {}
    if (key == newKey) {
    tempObj[key] = students[num][key]
    tempObj["date"] = students[num]["date"]
        data.push(tempObj);
    // console.log(data)
    }
}

function filter(arr) {
  console.log(arr)
  let temp;
  for(let i = 0; i < arr.length; i++) {
    temp = arr[i]
    arr.splice(0, 1)
    console.log(temp)
    console.log(arr)
  }
}

编辑:不确定它是否重要但在后端我使用 NodeJs 和 express mongoDB

这是我想出的数据转换函数。数据输出是一个对象数组,格式为 { x: 'YYYY-MM-DD', y: { [key]: score, [key]: score, ... } },每个日期都是唯一的,并从最旧到最新排序。

    function formatData(dataIn) {
        // step 1, sort input array by date, filter unnecessary data, pre-format
        // input and build a template object of all possible score keys
        const scoreObj = {};
        const data = dataIn
            .sort((a, b) => a.date.localeCompare(b.date))
            .map((d) => {
                const y = {};
                Object.keys(d).forEach((k) => {
                    if (k === 'email' || /comment/i.test(k)) {
                        delete d[k];
                    } else if (k !== 'date') {
                        if (!scoreObj.hasOwnProperty(k)) {
                            scoreObj[k] = 0;
                        }
                        y[k] = d[k];
                    }
                })
                return {
                    x: d.date,
                    y,
                };
            });
        // step 2, reduce sorted/filtered/formatted input array into array
        // of unique date entries (x) with associated score object (y)
        const dataOut = data.reduce((acc, d, i) => {
            // if date entry already exists
            if (i > 0 && acc[acc.length - 1].x === d.x) {
                for (const [k, v] of Object.entries(d.y)) {
                    acc[acc.length - 1].y[k] += v;
                }
                return acc;
            }
            // if new date entry
            return [ ...acc, {
                x: d.x,
                y: Object.assign({ ...scoreObj }, d.y),
            } ];
        }, [])
        return dataOut;
    }

转换功能分为两个不同的步骤。 Step 1 排序预处理数据,by:

  • 按日期升序对数据排序
  • 过滤 unwanted/unnecessary 键(email 和包含 comment 的任何内容(忽略大小写))
  • pre-formatting 数据进入形状为 { x: <date>, y: { <key/score pairs> } }
  • 的对象
  • 正在创建模板,zero-score 包含所有可能得分键的对象

然后第 2 步接收 pre-processed 数据,并使用 Array.reduce 按唯一日期对条目进行分组,并计算该日期的分数。即,对于每个输入条目,可能有两种结果:

  • 该日期不存在条目;创建日期的新条目,将模板得分对象与输入条目的实际得分值合并(这保证所有可能的得分键都存在于任何给定的唯一日期,即使它们的值可能为 0);然后将此条目推送到输出数组
  • 该日期已有一个条目;因为输入数组已经排序,我们只需要检查写入输出数组的最后一个条目以查看日期条目是否已经存在(我们只从第二个输入条目开始这样做,因为输出数组将是空的第一个输入条目被评估);然后我们将得分值添加到现有条目的得分键并继续下一个输入条目

输出数据非常灵活,您可以选择将所有分数键放入一个图中,或者每个分数键一个图(使用 map)。

最后,在 Svelte 中,给定一个名为 students 的输入数组,您可以使用单个反应式语句保留输出数据 up-to-date: $: graphData = formatData(students);

请参阅 this REPL 了解基本示例(没有图表,只有原始数据输出)。