Javascript 通过对记录进行分组来获取数组的映射

Javascript 通过对记录进行分组来获取数组的映射,javascript,arrays,dictionary,d3.js,Javascript,Arrays,Dictionary,D3.js,我正在尝试用D3转换一些数据,以获得按月份分组的平均每天数量(数组图)。数据如下所示: data = [ {day: "mon", amount: "4", month: "jan"}, {day: "tue", amount: "2", month: "jan"}, {day: "wed", amount: "3", month: "jan"}, {day: "wed", amount: "1", month: "jan"} ] {"jan": [ {

我正在尝试用D3转换一些数据,以获得按月份分组的平均每天数量(数组图)。数据如下所示:

data = [
    {day: "mon", amount: "4", month: "jan"},
    {day: "tue", amount: "2", month: "jan"},
    {day: "wed", amount: "3", month: "jan"},
    {day: "wed", amount: "1", month: "jan"}
]
{"jan": [
    {day:"mon", avg_amount:"4"},
    {day:"tue", avg_amount:"2"},
    {day:"wed", avg_amount:"2"}
]}
我试图获得的输出应如下所示:

data = [
    {day: "mon", amount: "4", month: "jan"},
    {day: "tue", amount: "2", month: "jan"},
    {day: "wed", amount: "3", month: "jan"},
    {day: "wed", amount: "1", month: "jan"}
]
{"jan": [
    {day:"mon", avg_amount:"4"},
    {day:"tue", avg_amount:"2"},
    {day:"wed", avg_amount:"2"}
]}
我已经尝试过使用D3的group和rollup函数,但是我只能得到一张地图,这并不是我想要的

d3.rollup(data, 
    v => d3.mean(v, d => d.amount), 
    d => d.month, d => d.day);
我是D3新手,所以我不确定如何才能达到这样的效果。使用D3有什么简单的方法可以做到这一点吗?

一个选项(不使用
D3.js
)是首先使用创建一个按月份和日期分组的结构,并按每个组获取总金额。完成此操作后,我们可以使用和映射结果结构以匹配您的预期输出:

const数据=[
{日期:“周一”,金额:“4”,月份:“一月”},
{日期:“星期二”,金额:“2”,月份:“一月”},
{日期:“星期三”,金额:“3”,月份:“一月”},
{日期:“星期三”,金额:“1”,月份:“一月”},
{日期:“周一”,金额:“5”,月份:“二月”},
{日期:“周一”,金额:“7”,月份:“二月”},
{日期:“星期二”,金额:“9”,月份:“2月”},
{日期:“星期二”,金额:“7”,月份:“2月”}
];
//按月/日减少到组,并按组获取总金额。
让res=data.reduce((acc,{day,amount,month})=>
{
acc[月]=acc[月]|{};
acc[月][日]=acc[月][日]|{日,金额:0,计数:0};
会计科目【月】【日】。金额+=+金额;
acc[月][日].计数++;
返回acc;
}, {});
控制台日志(res);
//将先前的结果转换为最终结构。
for(输入res)
{
res[key]=对象值(res[key])
.map({day,amount,count})=>({day,avg_amount:amount/count}));
}
控制台日志(res)您可以使用和

这里的基本思想是首先将数据分为月份和日期。对于每个月和每一天,我们将金额相加,并保留一个名为
count
的属性来检查天数。现在使用map,我们将数据映射为月份和天数,并将金额更改为
amount\u avg
let data=[{天:“周一”,金额:“4”,月:“一月”},{天:“周二”,金额:“2”,月:“一月”},{天:“周三”,金额:“3”,月:“一月”},{天:“周三”,金额:“1”,月:“一月”}]
让output=data.reduce((op,{day,amount,month})=>{
if(op[月]&op[月][日]){
op[月][日][金额]+=parseInt(金额)| | 0
op[月][日][计数]++
}否则{
如果(!op[月]){
op[月份]={}
}
op[月][日]={日,金额:parseInt(金额)| | 0,计数:1}
} 
返回操作
},{})
让所需=对象.keys(输出).map(el=>{
return({[el]:Object.values(output[el]).map({day,amount,count})=>({day,avg_amount:(amount/count).toFixed(2}))
})})

console.log(所需[0])
尽管您的问题标题说“Map”,并且您在问题正文中多次说“Map”,但您还是将所需结果作为一个对象来编写,可能是因为很难在问题正文中编写一个。更好的方法可以是:

Map(1) {
    "jan" => [
        {day:"mon", avg_amount:"4"},
        {day:"tue", avg_amount:"2"},
        {day:"wed", avg_amount:"2"}
    ]
}
因此,我假设您实际上是在请求映射(特别是因为您使用的是返回映射的
d3.rollup
)。但是,为了安全起见,我将提供两种解决方案,一种是使用
d3.rollup
创建一个真实对象,另一种是使用
d3.nest
创建一个对象,以防万一

使用d3.rollup映射

如果您想要的确实是一个映射,只需更改d3中的reduce函数。rollup

d3.rollup(iterable, reduce, ...keys)
//this part ----------^
或者,在您的情况下:

d3.rollup(data, 
    v => d3.mean(v, d => d.amount),//this is the reduce 
    d => d.month, d => d.day);//2 keys here
这是我将使用的reduce:

v => v.reduce((a, b) => {
    const found = a.find(d => d.day === b.day);
    if (!found) {
        a.push({
            day: b.day,
            avg_amount: +b.amount
        })
    } else {
        found.avg_amount = (found.avg_amount + (+b.amount)) / 2
    };
    return a;
}, [])
顺便说一句,你有两个键,放下最后一个。最后,记住你有字符串,而不是数字。据此胁迫他们

这是一个演示。不要使用代码段控制台(它将只显示
{}
),请检查浏览器的控制台:

数据=[{
日:“星期一”,
金额:“4”,
月份:“一月”
},
{
日期:“星期二”,
金额:“2”,
月份:“一月”
},
{
日:“星期三”,
金额:“3”,
月份:“一月”
},
{
日:“星期三”,
金额:“1”,
月份:“一月”
}
];
常量嵌套=d3.汇总(数据,
v=>v.reduce((a,b)=>{
const found=a.find(d=>d.day==b.day);
如果(!找到){
a、 推({
日:b日,
平均金额:+b.金额
})
}否则{
found.avg_amount=(found.avg_amount+(+b.amount))/2
};
返回a;
}, []),
d=>d.month);
console.log(嵌套)

这就是我想到的

它与其他答案有点不同,因为它有一个完整的结构,预先填充了0个平均量。这可能很有用,因为检查没有条目的月份和天数不会返回未定义的结果。它还单独存储所有条目,以防万一。如果它以某种方式破坏了某些东西,你当然可以用一种方式来扔它——在计算平均数之后,也就是说

好的,下面是条目:

data = [
 {day: "mon", amount: "4", month: "jan"},
 {day: "tue", amount: "2", month: "jan"},
 {day: "wed", amount: "3", month: "jan"},
 {day: "wed", amount: "1", month: "jan"}
];
然后,我们构建R对象来保存平均值。您可以简单地手工完成,但是将day元素写84次将相当乏味。此外,这使得更改月份和日期的名称非常容易-例如,当您需要使用不同的语言时

var m, M=['jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec'];
var d, D={'mon':0,'tue':1,'wed':2,'thu':3,'fri':4,'sat':5,'sun':6};
var R = {};
for(m=0;m<12;m++){
 R[M[m]] = []; // M[0] = jan,...
 for(d in D){ // d = mon,...
  R[M[m]][D[d]] = {day:d,avg_amount:0,all:[]}; // D[d] = 0,...
  // R = { jan:[ {day:'mon',avg_amount:0,all:[]},...
 }
}

因为除非这个特定的结构被强加给你,否则像这样混合对象和数组对我来说似乎有点笨拙。它可以变得更整洁。只是一个建议

R.jan[0].avg_value; // avg_amount of january, monday
vs

{"jan":{"mon":5,"tue":3}}
R.jan[0].avg_value; // avg_amount of january, monday
R.jan.mon; // avg_amount of january, monday