-3
\$\begingroup\$

From the array $data, I place a price for each month for one category in the first array. If no data is found for the particular category for a month, I insert 0, another category in the second array and finally place the arrays in a single array.

This code does so, but I believe there is a better and simpler way.

$data = [
        {"Price": "150", "DATE": "January", "ExpenseType": "Food"},
        {"Price": "3000", "DATE": "January", "ExpenseType": "House Rent"},
        {"Price": "130", "DATE": "February", "ExpenseType": "Beverage"},
        {"Price": "90", "DATE": "February", "ExpenseType": "Food"},
        {"Price": "1200", "DATE": "February", "ExpenseType": "House Rent"},
        {"Price": "200", "DATE": "March", "ExpenseType": "Cloth"}
    ]


    function ChartLabelLegend() {
        var $label = [];
        var $month = [];
        $.each($data, function (k, v) {
            if ($.inArray(v.DATE, $month) == -1) {
                $month.push(v.DATE);
            }
            if ($.inArray(v.ExpenseType, $label) == -1) {
                $label.push(v.ExpenseType);
            }
        });

        return [$label, $month];
    }

    function DataSeries() {
        $temp="";
        $count=0;
        var $dataSeries = [];
        var $i = -1;
        var $j=0;
        var $month = ChartLabelLegend()[1];
        var $label = ChartLabelLegend()[0];
        $.each($label, function (key, value) {
            $i++;
            $dataSeries.push([]);

                $.each($data, function (k, v) {

                        if (value == v.ExpenseType && $month[$j] == v.DATE) {
                            $dataSeries[$i].push(v.Price);
                            $temp=$month[$j];
                            $j++;

                        }
                        else {

                            if($month[$j] != v.DATE && $temp!=v.DATE ){
                                $dataSeries[$i].push(0);
                                $j++;
                                if($month[$j]== v.DATE && value== v.ExpenseType){
                                    $dataSeries[$i].push(v.Price);
                                    $j++;
                                }
                            }

                            if(k==$data.length-1 && $dataSeries[$i].length<$month.length ){
                                $dataSeries[$i].push(0);
                                $j++;
                            }

                        }

                });

            $j=0;
            $temp="";
        });
        return $dataSeries;
    }
\$\endgroup\$
4
\$\begingroup\$

The below code does so

This is not true, so your question should have been put on hold!

In fact, as already noticed by @RomanPerekhrest, your current code returns this:

[
  [150,90,0],
  [3000,1200,0],
  [0,130,0,0,0,0],
  [0,0,200,0]
]

while from your current example we can guess you wanted this:

[
  [150,90,0],
  [3000,1200,0],
  [0,130,0],
  [0,0,200]
]

Nevertheless, since I already had to examine it, why not give a review...

First step: about best practices

  1. Cosmetics (but not negligable!)
    • You should take care of consistently add spaces where needed, e.g. writìng:
      $temp = ""; rather than $temp="";, and so on
      if (k == $data.length-1) { instead of if(k==$data.length-1){, and so on
    • You shouldn't prepend variable names with "$" when they don't hold jQuery objects (which is the case of ALL your variables here).
  2. Performance
    • Avoid repeating the same code several times when possible, such as $j = 0; and $temp = ""; which are present first before entering your $.each() loop then just before ending it, or $j++ which is written 4 times while 2 times is enough.
    • Don't declare unused variables, like $count = 0;.
    • Don't create a function such as ChartLabelLegend(): it returns an array like [a, b], while you furtherly need separatly a and b: this way you have to successively call ChartLabelLegend()[0] and ChartLabelLegend()[1], so executing the contained $.each() 2 times!
      Look at the modified snippet below to see how to avoid that.
  3. Efficiency
    Don't pollute global space with variables when not needed: take care of declaring variables with var (omitted in the case of $j and $temp.
  4. Readability
    Especially in this case where your resulting arrays are somewhat "anonymous" (they lost the object property names of the initial array) it worth to choose significant names to help interpreting what the code does. E.g.:
    • Don't register ExpenseType in a label array iterated with key, value; instead register it as expTypes then iterate with expTypeKey, expType.
    • Similarily iterate data with dataKey, dataObj rather than k, v.

Taking into account all the above remarks leads to an improved version, like this:

var data = [
  {"Price": "150", "DATE": "January", "ExpenseType": "Food"},
  {"Price": "3000", "DATE": "January", "ExpenseType": "House Rent"},
  {"Price": "130", "DATE": "February", "ExpenseType": "Beverage"},
  {"Price": "90", "DATE": "February", "ExpenseType": "Food"},
  {"Price": "1200", "DATE": "February", "ExpenseType": "House Rent"},
  {"Price": "200", "DATE": "March", "ExpenseType": "Cloth"}
];

function DataSeries() {
  var expTypes = [];
  var dates = [];
  $.each(data, function(k, dataObj) {
    if ($.inArray(dataObj.DATE, dates) == -1) {
      dates.push(dataObj.DATE);
    }
    if ($.inArray(dataObj.ExpenseType, expTypes) == -1) {
      expTypes.push(dataObj.ExpenseType);
    }
  });
  
  var dataSeries = [];
  var i = -1;
  $.each(expTypes, function(expTypeKey, expType) {
    i++;
    var j = 0;
    var temp = "";
    dataSeries.push([]);
    
    $.each(data, function(dataKey, dataObj) {
      if (expType == dataObj.ExpenseType && dates[j] == dataObj.DATE) {
        dataSeries[i].push(dataObj.Price);
        temp = dates[j];
        j++;
      } else {
        if (dates[j] != dataObj.DATE && temp != dataObj.DATE ){
            dataSeries[i].push(0);
            j++;
            if (dates[j] == dataObj.DATE && expType == dataObj.ExpenseType){
              dataSeries[i].push(dataObj.Price);
              j++;
            }
        }
        if (dataKey == data.length-1) {
          dataSeries[i].push(0);
          j++;
        }
      }
    });
  });
  return dataSeries;
}

document.write(
  `[<br>${DataSeries().reduce((result, current) => `${result}&nbsp;&nbsp;[${current}]<br>`,'')}]`
)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Second step: logic

As demonstrated just above, your code doesn't work as expected. It comes from the way you try to fill empty DATEs by 0 after the last encountered not-empty DATE in each ExpenseType.

In fact, what you want to actually get is:

  • an array of as many subarrays as exist different ExpenseTypes found in the original data.
  • where each subarray contains one item for each different DATE found in the original data.
  • with each item value either set to the Price value in the corresponding couple ExpenseType, DATE from the original data, or 0 if it doesn't exist.

In other words it closely matches what was suggested by the name of your ChartLabelLegend() function: it's a matrix where lines come from ExpenseTypes, and columns from DATEs.

So the most reliable way to achieve this work is to follow this direction, noticing that the expected result should have lines ordered by ExpenseTypes and columns by DATE.

Then the schematic work becomes:

  1. Build an ordered set of the DATEs found in the original data, but use a months reference to ensure natural order, rather than alphabetical one (assuming that in the real life data is not guaranteed to be sorted on DATEs).
  2. Sort the original data by ExpenseType.
  3. Finally iterate it creating the expected result:
    • when encountering a set of objects with new ExpenseType, immediately create the corresponding "line", adding an array of as much items than registered different DATEs, each one set to 0.
    • then for each object in the current set, replace value of the corresponding DATE by the found Price.

Here is a snippet working as described above:

var data = [
  {"Price": "150", "DATE": "January", "ExpenseType": "Food"},
  {"Price": "3000", "DATE": "January", "ExpenseType": "House Rent"},
  {"Price": "130", "DATE": "February", "ExpenseType": "Beverage"},
  {"Price": "90", "DATE": "February", "ExpenseType": "Food"},
  {"Price": "1200", "DATE": "February", "ExpenseType": "House Rent"},
  {"Price": "200", "DATE": "March", "ExpenseType": "Cloth"}
];

function DataSeries() {
  var months = [
      'January', 'February', 'March', 'April', 'May', 'June', 'July',
      'August', 'September', 'October', 'November', 'December'
      ],
      dates = [];
  
  // Build an ordered list of dates
  for (dataObj of data) {
    if (!dates.includes(dataObj.DATE)) {
      dates.push(dataObj.DATE);
    }
  }
  dates.sort(function(a, b) {
    switch (true) {
      case months[a] > months[b]: return 1;
      case months[a] < months[b]: return -1;
      default: return 0;
    }
  });
  
  // Sort data by ExpenseType|DATE
  data.sort(function(a, b) {
    switch (true) {
      case a.ExpenseType > b.ExpenseType: return 1;
      case a.ExpenseType < b.ExpenseType: return -1;
      default: return 0;
    }
  });
  
  // Build required chart
  var dataSeries = [],
      expType = '',
      typeIndex;
  for (dataObj of data) {
    // Look for new ExpenseType
    if (dataObj.ExpenseType != expType) {
      expType = dataObj.ExpenseType;
      typeIndex = dataSeries.length;
      // Populate current line with all 0 columns
      dataSeries.push(new Array(dates.length).fill(0));
    }
    // Set price for encountered DATE
    dataSeries[typeIndex][dates.indexOf(dataObj.DATE)] = dataObj.Price;
  }
  return dataSeries;
}

document.write(
  `[<br>${DataSeries().reduce((result, current) => `${result}&nbsp;&nbsp;[${current}]<br>`,'')}]`
)

\$\endgroup\$
  • \$\begingroup\$ Sorry the code was not totally correct prevoiusly, though it was functional. Anyway thanks, It was a great help indeed :D \$\endgroup\$ – user35 Feb 22 '16 at 9:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.