0

I have an ajax call that returns a 2D object and I want to be able to convert it into a .csv file for the user to download.

The php code: echo json_encode($results);

The js function, right now has console.log (JSON.parse(res)); so I can verify the contents.

The console log shows:

Object {color: Object, animal: Object}
  color: Object
    1: "red"
    2: "white"
    3: "blue"
  animal: Object
    1: "cat"
    2: "dog"
    3: "mouse  

Each of the inner objects always has the same number of elements (in this case, 3)

I would like to convert this to a csv and then download. The first line of the csv would contain the outer object keys (color, animal) The csv would end up like this:

"color, animal"
"red, cat"
"white, dog"
"blue, mouse"

With the solutions I've looked at, they append a joined version of the object to each row. I need to transpose the data vertically.

I think that I need to step through each inner object (get the nth element of each one, get the n+1th element of each one etc) and build my own csv strings, but that seems awfully clunky.

Any nifty array/object functions out there that can help me?

I am able to create and download a .csv file, it's just this data transpose logic that has me stuck.

EDIT: I am not trying to convert a csv string to an array, nor am I trying to transpose a 2D array (it's a 2D object).

Clarification In my ajax call to the php file, I am sending an array of ["color", "animal"] which is derived from user input. The php file reads this array and gathers data based on this array. Once the data has been gathered, it returns a 2D array, the inner array being the new data. Thus:

[color[red, white, blue], animal[cat,dog,mouse]]
limeygent
  • 416
  • 6
  • 17
  • possible duplicate of [Javascript: convert CSV to separate arrays for keys and values](http://stackoverflow.com/questions/30049406/javascript-convert-csv-to-separate-arrays-for-keys-and-values) – Jaffer Wilson Jul 01 '15 at 02:49
  • Not a dup. See my edit comment above. – limeygent Jul 01 '15 at 03:24
  • Could you also post the json data sent by the php code? (`console.log(res)`) – Kaiido Jul 01 '15 at 03:45
  • What is a 2D object? – Lucien Stals Jul 01 '15 at 03:46
  • Here is the raw output (json data) @Kaiido `{"color":{"1":"red","2":"white","3":"blue"},"animal":{"1":"cat","2":"dog","3":"mouse"}}` – limeygent Jul 01 '15 at 03:51
  • @LucienStals, in this question, a 2D object is an object of objects. In this case, the outer object contains 2 objects. In my previous comment, I give the JSON representation of it. – limeygent Jul 01 '15 at 03:53

3 Answers3

1

From your question you are saying console.log($result) that means you are trying to fetch it via AJAX I assume.

I would recommend you to not create CSV on client side JavaScript, do it from your PHP code, simply put a hyperlink for your php file which contains the CSV creation code with Content-Type and Content-Disposition therefore the browser will directly show the file save dialog box.

See this answer File is not being downloaded when requested through Ajax using JQuery

Simply put a hyperlink

<a href="admin_ajax.php">Download</a>

Refer Force Download CSV File
Refer Create a CSV File for a user in PHP

-- EDIT --

Client side CSV is possible.

But the reason I am recommending the server side CSV.

  • Size of CSV can be bigger which might make your client size JSON based loop consume memory and might reduce the browser performance.
  • Server side compression options like GZIP or reverse proxy compressed traffic is easy and it will be automatically decompressed by browser, but client side decompression could be trivial
  • In case if you want to provide an additional option of PDF or Rich XLSX file download option it may not be possible as easy in javascript.
Community
  • 1
  • 1
Dickens A S
  • 3,824
  • 2
  • 22
  • 45
  • from those links, it seems all the data is collected on the server side and then returned to the client. In my case, I need to pass some user defined inputs (color & animal in this case) before the server can determine what data to collect and return. Correct me if I'm wrong, I don't know if this will help me. It's an interesting strategy I'll store for future use! Maybe I can append the user inputs in a GET request, modifying the hyperlink. `admin_ajax.php?a=color&b=animal` with a bit of js. – limeygent Jul 01 '15 at 03:45
  • you go for
    tag hidden and put all you hidden fields and populate it and submit it actually, but getting the JSON and forming the CSV as client side may not be better approach, that was my point
    – Dickens A S Jul 01 '15 at 07:42
  • why do you not recommend creating a csv on the client side? I tested your solution so that I could see how it works, so now I have another strategy I can use in the future. Appreciate the suggestion. I upvoted the answer, because it did provide a solution, as long as I reworked the php code to transpose the data. – limeygent Jul 02 '15 at 00:07
1

You could try something like that :

var rep = JSON.parse('{"color":{"1":"red","2":"white","3":"blue"},"animal":{"1":"cat","2":"dog","3":"mouse"}}')
function toCSV(jsonObject) {
  var csv = [];
  for (var i in jsonObject) {
    if (jsonObject.hasOwnProperty(i)) {
      var title = i.indexOf(' ')>-1? '"'+i+'"': i;
      csv[0] ? csv[0].push(title) : csv[0] = [title];
      var k = Object.keys(jsonObject[i]);
      for (var j = 0; j < k.length; j++) {
        var val = jsonObject[i][k[j]].indexOf(' ')>-1? '"'+jsonObject[i][k[j]]+'"': jsonObject[i][k[j]];;
        csv[j+1] ? csv[j + 1].push(val) : csv[j+1] = [val];
      }
    }
  }
  return csv.join('\n');
}

document.body.innerHTML = toCSV(rep).replace(/\n/g, '<br>');
Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • the JSON data is `{"color":{"1":"red","2":"white","3":"blue"},"animal":{"1":"cat","2":"dog","3":"mouse"}}` – limeygent Jul 01 '15 at 03:54
  • Getting close - now that I'm returning more data (in csv, it should show up as 4 columns of data, each with 1 row of header & 20 rows of data), some of the input data is not making it to the output. I think some of the index counters are slightly off. Ex row 1 (header): 4 entries, rows 2-7: 4 entries, rows 8-14: 3 entries, row 15: 1 entry, rows 16-18: 1 entry, rows 19-21: 0 entries. – limeygent Jul 01 '15 at 04:30
  • once again, could you post the raw json somewhere like in a [pastebin](http://pastebin.com/) so I can update? – Kaiido Jul 01 '15 at 04:33
  • Oh and rereading your comment I feel that the problem might indeed be the empty inputs. Is there a way that your php code always outputs a string, even empty? (`20:""`) – Kaiido Jul 01 '15 at 04:37
  • All of the data is non-blank. There seems to be a pattern. After the first 7 rows in the first column, it stops processing. After 14 rows in the 2nd column, it stops processing, then 18, then 15. I used different data, but same results. [link to pastebin](http://pastebin.com/DDyAyK9e) – limeygent Jul 01 '15 at 04:53
  • @limeygent Ok there was indeed an odd bug, update should have fixed it. I also added a csv validation step that adds `"` at the begining and the end of values which contains white spaces – Kaiido Jul 01 '15 at 05:37
1
var k = JSON.parse('{"color":{"1":"red","2":"white","3":"blue"},"animal":{"1":"cat","2":"dog","3":"‌mouse"}, "price":{"1":"1000","2":"2000","3":"3000"}}');

var finalStr = '';
//Extract Top Columns Name
var keyList = [];
for(each in k) {
    keyList.push(each)
}
//Join them accordingly
finalStr = '"' + keyList.join(", ") + '"\n'

//As you said - Each of the inner objects always has the same number of elements (in this case, 3)
var startRef = k[keyList[0]];

//for each key in ref object
for(each in startRef) {
    var arr = [];
    //extract value for that key in each child object 
    for(var i=0;i<keyList.length;i++) {
        arr.push(k[keyList[i]][each])
    }
    //Join them accordingly
    finalStr += '"' + arr.join(", ") + '"\n'
}
console.log(finalStr)

//Output
"color, animal, price"
"red, cat, 1000"
"white, dog, 2000"
"blue, ‌mouse, 3000"
Harpreet Singh
  • 2,651
  • 21
  • 31