从 foreach 创建动态 JSON

Posted

技术标签:

【中文标题】从 foreach 创建动态 JSON【英文标题】:Create dynamic JSON from foreach 【发布时间】:2015-07-18 16:31:01 【问题描述】:

我使用jquery flot charts 来表示我的数据。这是我制作的示例 JSFiddle,它显示了图表所需的 JSONS 的外观。

数据源来自 mysql 存储过程,其输出示例如下:

我需要在图表中表示 count 值堆叠在 y 轴上的不同 innumber 上,name 值在 x 轴上,在另一个图表中,outnumber 的值. (在堆积条形图中)。

-数据系列应匹配,因此特定标签应针对客户显示。

这是我目前拥有的 php

$query = $this->db->query("call GetAllCustomersV2($id, $year, $month, $day)");
$customers = $query->result_array();

foreach ($customers as $customer) 

  if($customer['innumber'] != null)

      $chartInbound['name'] = $customer['name'];
      $chartInbound['label'] = $customer['innumber'];
      $chartInbound['count'] = $customer['count'];
      $chartInbound['customerid'] = $customer['id'];

      array_push($out['chartInbound'], $chartInbound);
   

   if($customer['outnumber'] != null)

      $chartOutbound['name'] = $customer['name'];
      $chartOutbound['label'] = $customer['outnumber'];
      $chartOutbound['count'] = $customer['count'];
      $chartOutbound['customerid'] = $customer['id'];

      array_push($out['chartOutbound'], $chartOutbound);
   

print_r($out['chartInbound']); 的输出为:

Array
(
[0] => Array
    (
        [name] => 1st Online Solutions
        [label] => 01-02
        [count] => 577
        [customerid] => 129
    )

[1] => Array
    (
        [name] => Bookngo
        [label] => 01-02
        [count] => 2
        [customerid] => 95
    )

[2] => Array
    (
        [name] => Boutixury
        [label] => 07
        [count] => 1
        [customerid] => 14
    )

[3] => Array
    (
        [name] => Cruise Village
        [label] => 01-02
        [count] => 16
        [customerid] => 25
    )

[4] => Array
    (
        [name] => Cruise Village
        [label] => 00
        [count] => 1
        [customerid] => 25
    )

[5] => Array
    (
        [customer] => Cruise Village
        [label] => 07
        [countInbound] => 16
        [minsInbound] => 125
        [customerid] => 25
    )
  ...................
)

print_r(json_encode($out['chartInbound'])); 的输出是:

[

    "name": "1st Online Soultions"
    "label": "01-02",
    "count": "577",
    "customerid": "129"
,

    "name": "Bookngo"
    "label": "01-020",
    "count": "2",
    "customerid": "129"
,

    "name": "Boutixury"
    "label": "07",
    "count": "1",
    "customerid": "14"
,

    "name": "Cruise Village"
    "label": "07",
    "count": "16",
    "customerid": "25"
,
 .................
]

但这不是很有帮助。 问:如何根据查询数据创建上述 jsfiddle 中显示的动态 JSON?

【问题讨论】:

你的问题中的数据(来自MySql的数据)和你的jsfiddle中的数据应该匹配吗?? chartDatachartTicks 的格式应该相同,并且它们的值应该动态填充 可以修改存储过程,还是固定?是否可以通过 SQL 语句直接攻击数据?如果是的话,能否提供数据结构,或者只是修改获取数据的方式,就好像获取JSON数据一样。 【参考方案1】:

使用循环遍历您的数据并构建 newDatanewTicks 数组以供 flot 使用:

var newData = [];
var newLabels = []; // only used to get index since newData has objects in it
var newTicks = [];

for (var i = 0; i < dataFromServer.length; i++) 
    var datapoint = dataFromServer[i];

    var tick = newTicks.indexOf(datapoint.name);
    if (tick == -1) 
        tick = newTicks.length;
        newTicks.push(datapoint.name);
    

    var index = newLabels.indexOf(datapoint.label);
    if (index == -1) 
        index = newLabels.length;
        newLabels.push(datapoint.label);

        newDataPoint = 
            label: datapoint.label,
            data: []
        ;
        newDataPoint.data[tick] = [tick, datapoint.count];
        newData.push(newDataPoint);
     else 
        newData[index].data[tick] = [tick, datapoint.count];
    

for (var i = 0; i < newTicks.length; i++) 
    newTicks[i] = [i, newTicks[i]];

newLabels = null;

我还必须更改您的工具提示生成,因为您的代码仅在所有数据序列都完成并排序后才有效。现在也更简单了。

complete fiddle

【讨论】:

你能不能对下面的 JsFiddle 做一个小的更新,以在情节点击事件(条形点击事件)上获取 customerid。我已将绘图单击绑定功能添加到 jsFiddle:jsfiddle.net/t1jgvyLx 见updated fiddle。我们在数据构建循环中填充一个customerIds 对象,并在工具提示创建中使用它。 我实际上在绘图点击绑定函数中需要它,所以我可以使用 customerid 作为 URI 参数重定向页面,我不需要它在工具提示中。 我想我在您的解决方案中发现了一个问题。问题是堆叠条重叠/有时不显示所有标签。可能是因为该系列的长度不同,并且如果标签没有数据,则它不包含 0。你能检查一下吗?如果需要,我会创建一个新帖子,谢谢。 另外,我这边有一个问题,相同的name 应该有相同的customerid。见jsfiddle.net/d7gah2wd/1【参考方案2】:

只是一个想法,我想您在存储过程中使用了 group by。如果您可以修改它并添加 WITH ROLLUP,那么数据库将为您计算计数...请参阅https://dev.mysql.com/doc/refman/5.0/en/group-by-modifiers.html 或搜索 SO 以获取建议

【讨论】:

谢谢,这是一些有用的信息,我已将其添加到存储过程中!【参考方案3】:

您可以在客户端执行此操作(尽管理想情况下应该在服务器端执行),方法是:

var table = [
    name: 'a', label: 'l1', count: '15', customerid: '1',
    name: 'a', label: 'l2', count: '1', customerid: '1',
    name: 'a', label: 'l3', count: '7', customerid: '1',
    name: 'b', label: 'l1', count: '3', customerid: '2',
    name: 'b', label: 'l2', count: '9', customerid: '2',
    name: 'b', label: 'l3', count: '2', customerid: '2',
    name: 'c', label: 'l1', count: '1', customerid: '3',
    name: 'c', label: 'l2', count: '7', customerid: '3',
    name: 'a', label: 'l3', count: '5', customerid: '4',
    name: 'a', label: 'l2', count: '6', customerid: '4'
];

var customers = ;
var labels = ;

var i;
for (i = 0; i < table.length; ++i) 
    customers[table[i].customerid] = table[i].name;
    labels[table[i].label] = labels[table[i].label] || [];
    labels[table[i].label].push([+table[i].customerid, +table[i].count]);


var chartData = [];
var chartTicks = [];

for (customer in customers) 
    if (customers.hasOwnProperty(customer)) 
        chartTicks.push([+customer, customers[customer]]);
    

for (label in labels) 
    if (labels.hasOwnProperty(label)) 
        chartData.push(label: label, data: labels[label]);
    

它考虑了具有相同名称的不同客户(不同的 customerid)(尽管 Flot 并不能很好地处理这一点),以及缺少某些标签数据的客户。将此逻辑转移到 PHP 中并在服务器端执行应该不会太难。

编辑: 好的,我没有注意到当有 labelID“间隙”时它的行为很奇怪。这是修改后的代码:

var table = [
    name: 'a', label: 'l1', count: '15', customerid: '1',
    name: 'a', label: 'l2', count: '1', customerid: '1',
    name: 'a', label: 'l3', count: '7', customerid: '1',
    name: 'b', label: 'l1', count: '3', customerid: '2',
    name: 'b', label: 'l2', count: '9', customerid: '2',
    name: 'b', label: 'l3', count: '2', customerid: '2',
    name: 'c', label: 'l1', count: '1', customerid: '3',
    name: 'c', label: 'l2', count: '7', customerid: '3',
    name: 'a', label: 'l3', count: '5', customerid: '7',
    name: 'a', label: 'l2', count: '6', customerid: '7'
];

var customers = ;
var labels = ;

var chartData = [];
var chartTicks = [];

var i;
var customerNo = 0;
for (i = 0; i < table.length; ++i) 
    if(!customers.hasOwnProperty(table[i].customerid)) 
        customers[table[i].customerid] = table[i].name;
        chartTicks.push([customerNo, table[i].name]);
        customerNo++;
    
    labels[table[i].label] = labels[table[i].label] || [];
    labels[table[i].label].push([customerNo - 1, +table[i].count]);


for (label in labels) 
    if (labels.hasOwnProperty(label)) 
        chartData.push(label: label, data: labels[label]);
    

标签 ID 按照它们在来自服务器的表中出现的顺序给出。 (虽然它仍然区分了两个同名但customerID不同的客户)

【讨论】:

谢谢,但是上面的问题是flot期望chartTicks和数据的一系列数据,格式为“data”:[[0,577],[1,2],[2 ,16],[3,11],[4,22],[5,43]], .... 而不是 cusomerid 它应该是索引(图表中的客户位置)并且应该与客户匹配标签,就像在 jsfiddle 中一样——【参考方案4】:

您将不得不自己改造这些结构。您可以在服务器端或客户端执行此操作。在任何一种情况下,运行结果并构建您想要的结构。 小心尝试在 json 中编码 php 关联数组,并注意 NUMERIC_CHECK 的行为。

【讨论】:

对不起,你这么认为。我不知道你是否期待代码。【参考方案5】:

您在chartTicks[i] 中的数据点似乎需要与chartData[i].data 中的刻度顺序相匹配。 确保这种匹配的一种方法是在 sql 中按名称对数据进行排序,并在 php 中按客户第一和标签第二来堆叠结果。

$query = $this->db->query("call GetAllCustomersV2($id, $year, $month, $day)");
$customers = $query->result_array(); //should be sorted by name
$results = array();

foreach ($customers as $customer) 
    $i = is_array($results[$customer['name']][$customer['innumber']]) 
        ? count($results[$customer['name']][$customer['innumber']])
        : 0;

    //stack data points by customer name first and label second
    $results[$customer['name']][$customer['innumber']][] = array($i,$customer['count']);


$chartData = array();
$chartTicks = array();
$i=0;

foreach($results as $name => $labels) 
    $chartTicks[] = array($i++,$name);
    foreach($labels as $label => $data) 
        $chartData[] = array(
            'label' => $label,
            'data' => $data,
        );
    


print json_encode($chartData);
print json_encode($chartTicks);

【讨论】:

【参考方案6】:

这是将当前 JSON 数据结构转换为所需输出的简洁方法:

var reduced;
var chartData = Object.keys(reduced = data.reduce(function(a, b) 
  if(a[b.label]) 
    a[b.label].push([a[b.label].length, parseInt(b.count, 10)]);
   else 
    a[b.label] = [[0, parseInt(b.count, 10)]];
  
  return a;
, )).map(function(key) 
  return 
    label: key,
    data: reduced[key]
  ;
);

小提琴:http://jsfiddle.net/rdkgbteq/1/

如果您想在服务器上转换数据,PHP 中也是这样:

$reduced = array_reduce($data, function($result, $current) 
    if(array_key_exists($current['label'], $result)) 
        array_push($result, [count($result[$current['label']]), $current['count']]);
     else 
        $result[$current['label']] = [[0, $current['count']]];
    
    return $result;
, array());

$formatted = array_map(function($key) 
    return array(
        'label' => $key,
        'data'  => $reduced[$key]
    ); 
, array_keys($reduced));

echo json_encode($formatted);

如果您希望我详细说明这里发生的事情,请告诉我。

【讨论】:

以上是关于从 foreach 创建动态 JSON的主要内容,如果未能解决你的问题,请参考以下文章

从后面的代码调用动态创建的控件

数据属性无法从 foreach 中获取动态 ID

使用foreach从动态post数组插入sql

jquery动态ul foreach数组

动态 HTML 上的事件绑定,使用 fetch 和 forEach 内

无法编译 SwiftUI ForEach 表