使用D3 .rollup的嵌套JSON数组的总和

时间:2017-02-24 21:30:57

标签: javascript json d3.js

我正在尝试获取嵌套对象数组中每个键的总和和平均值,如下所示:

    var data = [
    {department:'Electro',quant:{M:30, T:20, W:51, R:22, F:35 }},
    {department:'Beauty'',quant:{M:50, T:32, W:75, R:61, F: 45}},
    {department:'Apparel'',quant:{M:62, T:42, W:135, R: 82, F:89}},
    {department:'Misc',quant:{M:89, T:54, W:103, T:94, F:90}}
];

所以我需要分别对每个部门的总和。即sum = {'Elecro':158,'Beauty':263}

我使用的是汇总方法,但examples不适用于嵌套数组。

var deptSum = d3.nest()
.key(function(d) { return d.dept; })
.rollup(function(v) { return {
    count: v.length,
    total: d3.sum(v, function(d) {return d.quant; }),
    avg: d3.mean(v, function(d) {return d.quant; })
}; })
.entries(data)

的console.log(JSON.stringify(deptSum))

但是给我0的总和。

2 个答案:

答案 0 :(得分:1)

你甚至不需要d3,这是一个纯粹的js解决方案:

{"Electro":158,"Beauty":263,"Apparel":410,"Misc":376}

会给你import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Random; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableCellRenderer; import com.sun.prism.impl.Disposer.Record; import javax.swing.JButton; import javax.swing.JTable; import javax.swing.table.DefaultTableModel; import javax.swing.JLabel; import javax.swing.SwingConstants; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Font; import java.awt.Toolkit; import javax.swing.JTextField; import javax.swing.JComboBox; import javax.swing.DefaultComboBoxModel; import traitement.CustomRenderer; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; public class TestSort extends JFrame implements ActionListener{ private JTable table1; private JButton btnSort; private int selectedCombCriteria=-1; private JComboBox combCriteria; private Boolean showSelection=false; public static void main(String[] args) { // TODO Auto-generated method stub TestSort ts=new TestSort(); ts.setVisible(true); } TestSort() { Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); this.setLocation(dim.width/2-this.getSize().width/2, dim.height/2-this.getSize().height/2); setSize(new Dimension(439, 325)); setPreferredSize(new Dimension(600,400)); JPanel pt=new JPanel(); getContentPane().add(pt); pt.setLayout(null); combCriteria = new JComboBox(); combCriteria.setModel(new DefaultComboBoxModel(new String[] {"Age", "Score"})); combCriteria.setSelectedIndex(0); combCriteria.setBounds(115, 205, 67, 20); combCriteria.addActionListener(this); pt.add(combCriteria); btnSort = new JButton("Sort"); btnSort.addActionListener(this); btnSort.setBounds(324, 204, 89, 23); pt.add(btnSort); JScrollPane scrollPane = new JScrollPane(); scrollPane.setBounds(20, 41, 393, 127); pt.add(scrollPane); table1 = new JTable(); SortModel sm=new SortModel(); table1.setModel(sm); table1.setBounds(50, 26, 329, 130); scrollPane.setViewportView(table1); table1.getSelectionModel().addListSelectionListener( new ListSelectionListener() { public void valueChanged(ListSelectionEvent event) { SortModel.selectedRecord=((SortModel)table1.getModel()).getRowAt(table1.getSelectedRow()); showSelection=true; } }); table1.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() { @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { final Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); c.setBackground(row==0?Color.YELLOW:isSelected&&showSelection?Color.lightGray:Color.white); return c; } }); JLabel lblSelectARow = new JLabel("Select a row to keep at the top and sort using one of the sorting criteria"); lblSelectARow.setFont(new Font("Traditional Arabic", Font.PLAIN, 14)); lblSelectARow.setHorizontalAlignment(SwingConstants.CENTER); lblSelectARow.setBounds(20, 11, 393, 23); pt.add(lblSelectARow); JLabel lblSortCreteria = new JLabel("Sorting criteria:"); lblSortCreteria.setBounds(20, 208, 96, 14); pt.add(lblSortCreteria); } @Override public void actionPerformed(ActionEvent e) { if(e.getSource()==btnSort) if(SortModel.selectedRecord!=null) { ((SortModel)table1.getModel()).sort(selectedCombCriteria); table1.repaint(); showSelection=false; } if(e.getSource()==combCriteria) { selectedCombCriteria=combCriteria.getSelectedIndex(); } } } class SortModel extends AbstractTableModel { public SortModel(){ data=fillOnce(); } private String[] columns = {"First Name", "Last Name", "Profession", "Age","Score","Comment"}; public List<MyDataRecord> data=new ArrayList<MyDataRecord>(); public static MyDataRecord selectedRecord; @Override public int getRowCount() { // TODO Auto-generated method stub return data.size(); } @Override public String getColumnName(int column) { return columns[column]; } @Override public int getColumnCount() { return columns.length; } public Object getValueAt(int rowIndex, int columnIndex) { switch (columnIndex) { case 0: return data.get(rowIndex).getfName(); case 1: return data.get(rowIndex).getlName(); case 2: return data.get(rowIndex).getProfession(); case 3: return data.get(rowIndex).getAge(); case 4: return data.get(rowIndex).getScore(); case 5: return data.get(rowIndex).getComment(); default: throw new IllegalArgumentException(); } } public MyDataRecord getRowAt(int rowIndex) { return data.get(rowIndex); } //sorting method public void sort(int sort) { //sort Collections.sort(data, new Comparator<MyDataRecord>() { public int compare(MyDataRecord object1, MyDataRecord object2) { if(sort!=1) return Integer.compare(object1.getAge(), object2.getAge()); else return Integer.compare(object1.getScore(), object2.getScore()); } }); //then remove he selected element data.remove(selectedRecord); //put the selected element on the top of the list if(selectedRecord!=null) { data.add(0, selectedRecord); } } public List<MyDataRecord> fillOnce() { List<MyDataRecord> tempList=new ArrayList<MyDataRecord>(); Random r = new Random(); for(int i=1;i<=6;i++) { MyDataRecord mdr=new MyDataRecord("FNtest"+i, "LNtest"+i, "PROFtest"+i, "COMtest"+i, r.nextInt(100-1) + 1, r.nextInt(100-1) + 1); tempList.add(mdr); } return tempList; } } class MyDataRecord{ private String fName,lName,profession,comment; private int age,score; public MyDataRecord(String fName, String lName, String profession, String comment, int age, int score) { super(); this.fName = fName; this.lName = lName; this.profession = profession; this.comment = comment; this.age = age; this.score = score; } public String getfName() { return fName; } public void setfName(String fName) { this.fName = fName; } public String getlName() { return lName; } public void setlName(String lName) { this.lName = lName; } public String getProfession() { return profession; } public void setProfession(String profession) { this.profession = profession; } public String getComment() { return comment; } public void setComment(String comment) { this.comment = comment; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getScore() { return score; } public void setScore(int score) { this.score = score; } }

请参阅https://jsfiddle.net/96msy7jd/1

答案 1 :(得分:1)

有一种D3方法可以获得汇总中的总和(以及平均值)。但问题是,d3.sumd3.mean都需要数组,但quant只是一个对象:

quant: {M:30, T:20, W:51, R:22, F:35 };

解决方案:使用D3方法获取名为d3.values的属性值。根据{{​​3}},它:

  

返回一个包含指定对象(关联数组)的属性值的数组。

因此,您的rollup应为:

.rollup(function(v) {
    return {
        count: v.length,
        total: d3.sum(d3.values(v[0].quant)),
        avg: d3.mean(d3.values(v[0].quant))
    };
})

这是一个演示:

&#13;
&#13;
var data = [
    {department:'Electro',quant:{M:30, T:20, W:51, R:22, F:35 }},
    {department:'Beauty',quant:{M:50, T:32, W:75, R:61, F: 45}},
    {department:'Apparel',quant:{M:62, T:42, W:135, R: 82, F:89}},
    {department:'Misc',quant:{M:89, T:54, W:103, T:94, F:90}}
];

var deptSum = d3.nest()
.key(function(d) { return d.department; })
.rollup(function(v) { return {
    count: v.length,
    total: d3.sum(d3.values(v[0].quant)),
    avg: d3.mean(d3.values(v[0].quant))
}; })
.entries(data)

console.log(deptSum)
&#13;
<script src="https://d3js.org/d3.v4.min.js"></script>
&#13;
&#13;
&#13;