Создание динамического JSON из foreach

Я использую jquery flot charts для представления моих данных. Вот пример JSFiddle Я сделал это, чтобы показать, как должны выглядеть JSONS, необходимые для диаграммы.

Источник данных из хранимой процедуры MySql, которая имеет пример ниже:

enter image description here

Мне нужно представить на диаграмме значения count, сложенные для разных innumber по оси Y, значения 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"
},
 .................
]

Но это не очень полезно.

Q: Как создать динамический JSON, указанный в приведенном выше jsfiddle, из данных запроса?

Ответы

Ответ 1

Просматривая данные с помощью цикла и создавая массивы newData и newTicks для использования флота:

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;

Мне также пришлось сменить генерацию всплывающей подсказки, так как ваш код работал только тогда, когда все данные были заполнены и отсортированы. Теперь это проще.

полная скрипка

Ответ 2

Вы можете сделать это на стороне клиента (хотя в идеале это должно быть сделано на стороне сервера), используя что-то вроде:

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]});
    }
}

В нем учитываются разные клиенты (разные клиенты) с тем же именем (хотя Flot на самом деле не справляется с этим) и клиенты с отсутствующими данными для некоторых ярлыков. Не следует слишком сильно переносить эту логику в PHP и делать это на стороне сервера.

EDIT: Хорошо, я не заметил, что это действует странно, когда есть метки "пробелы". Здесь пересмотренный код:

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]});
    }
}

Идентификаторы меток указываются в том порядке, в котором они отображаются в таблице, поступающей с сервера. (Хотя он по-прежнему различает двух клиентов с тем же именем, но с разными идентификаторами клиентов)

Ответ 3

Просто идея, я полагаю, вы используете группу в своей хранимой процедуре. Если вы можете изменить его и добавить WITH ROLLUP, в базе данных будет рассчитан счетчик... См. https://dev.mysql.com/doc/refman/5.0/en/group-by-modifiers.html или выполните поиск SO для предложения

Ответ 4

Вам придется преобразовать структуры самостоятельно. Вы можете делать это на стороне сервера или на стороне клиента. В любом случае выполните результаты и постройте нужную структуру. Будьте осторожны с попыткой кодирования ассоциативных массивов php в json и остерегайтесь поведения 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]
  };
});

Fiddle: 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);

Сообщите мне, если вы хотите, чтобы я расширил то, что происходит здесь.