2017-05-25 86 views
1

我有一個需要轉換成Javascript對象/ JSON文件的CSV文件。無論如何,因爲我將用JS來處理數據,並且無論如何都沒關係。將CSV轉換爲JavaScript中的嵌套JSON

因此,例如這樣的:

name,birthday/day,birthday/month,birthday/year,house/type,house/address/street,house/address/city,house/address/state,house/occupants 
Lily Haywood,27,3,1995,Igloo,768 Pocket Walk,Honolulu,HI,7 
Stan Marsh,19,10,1987,Treehouse,2001 Bonanza Street,South Park,CO,2 

應該成爲這樣的:

[ 
    { 
     "name": "Lily Haywood", 
     "birthday": { 
      "day": 27, 
      "month": 3, 
      "year": 1995 
     }, 
     "house": { 
      "type": "Igloo", 
      "address": { 
       "street": "768 Pocket Walk", 
       "city": "Honolulu", 
       "state": "HI" 
      }, 
      "occupants": 7 
     } 
    }, 
    { 
     "name": "Stan Marsh", 
     "birthday": { 
      "day": 19, 
      "month": 10, 
      "year": 1987 
     }, 
     "house": { 
      "type": "Treehouse", 
      "address": { 
       "street": "2001 Bonanza Street", 
       "city": "South Park", 
       "state": "CO" 
      }, 
      "occupants": 2 
     } 
    } 
] 

這就是我想出了:

function parse(csv){ 
    function createEntry(header){ 
     return function (record){ 
      let keys = header.split(","); 
      let values = record.split(","); 
      if (values.length !== keys.length){ 
       console.error("Invalid CSV file"); 
       return; 
      } 
      for (let i=0; i<keys.length; i++){ 
       let key = keys[i].split("/"); 
       let value = values[i] || null; 
       ///// 
       if (key.length === 1){ 
        this[key] = value; 
       } 
       else { 
        let newKey = key.shift(); 
        this[newKey] = this[newKey] || {}; 
        //this[newKey][key[0]] = value; 
        if (key.length === 1){ 
         this[newKey][key[0]] = value; 
        } 
        else { 
         let newKey2 = key.shift(); 
         this[newKey][newKey2] = this[newKey][newKey2] || {}; 
         this[newKey][newKey2][key[0]] = value; 
         //if (key.length === 1){} 
         //... 
        } 
       } 
       ///// 
       } 
     }; 
    } 
    let lines = csv.split("\n"); 
    let Entry = createEntry(lines.shift()); 
    let output = []; 
    for (let line of lines){ 
     entry = new Entry(line); 
     output.push(entry); 
    } 
    return output; 
} 

我的代碼工作,但有一個明顯的缺陷:對於每一層它進入(如house/address/street),我不得不手動編寫代表吃掉了if/else陳述。

有沒有更好的方法來寫它?我知道這涉及某種遞歸或迭代,但我似乎無法弄清楚如何。

我已經搜索過SO,但大部分問題似乎都是用Python而不是JS來完成的。

儘可能我希望在沒有任何其他庫的情況下在vanilla JS中完成此操作。

回答

1

您可以通過遞歸創建對象實現預期結果。
看一下下面的代碼:

var csv = [ 
 
    "name,birthday/day,birthday/month,birthday/year,house/type,house/address/street,house/address/city,house/address/state,house/occupants", 
 
    "Lily Haywood,27,3,1995,Igloo,768 Pocket Walk,Honolulu,HI,7", 
 
    "Stan Marsh,19,10,1987,Treehouse,2001 Bonanza Street,South Park,CO,2" 
 
]; 
 

 
var attrs = csv.splice(0,1); 
 

 
var result = csv.map(function(row) { 
 
    var obj = {}; 
 
    var rowData = row.split(','); 
 
    attrs[0].split(',').forEach(function(val, idx) { 
 
    obj = constructObj(val, obj, rowData[idx]); 
 
    }); 
 
    return obj; 
 
}) 
 

 

 
function constructObj(str, parentObj, data) { 
 
    if(str.split('/').length === 1) { 
 
    parentObj[str] = data; 
 
    return parentObj; 
 
    } 
 

 
    var curKey = str.split('/')[0]; 
 
    if(!parentObj[curKey]) 
 
    parentObj[curKey] = {}; 
 
    parentObj[curKey] = constructObj(str.split('/').slice(1).join('/'), parentObj[curKey], data); 
 
    return parentObj; 
 
} 
 

 
console.log(result);
.as-console-wrapper{max-height: 100% !important; top:0}

constructObj()功能基本上構建了結果對象遞歸通過查看列名,因此,如果列名包含/house/address/street它會創建一個名爲house的對象中的一個鍵,然後以遞歸方式自行調用其餘的字符串中的其餘鍵,即address/street/。當字符串中不再有/時,遞歸結束,然後它簡單地分配該鍵中的值並返回結果對象。

+0

謝謝,這正是我一直在尋找的! 有點偏離主題,爲什麼'attrs [0] .split(',')'而不是'attrs.split(',')'?不是'attrs'只是''名字,生日/日子,......「'? – rapinopo

+0

@rapinopo'.splice()'返回一個已刪除元素的數組。因此'attrs'將會是一個數組。 – abhishekkannojia

2

您可以將記錄在地圖和動態創建對象:

let records = ['Lily Haywood,27,3,1995,Igloo,768 Pocket Walk,Honolulu,HI,7', 
 
'Stan Marsh,19,10,1987,Treehouse,2001 Bonanza Street,South Park,CO,2'] 
 

 
let output = records.map(record => { 
 
\t 
 
\t let arr = record.split(',') 
 
\t 
 
\t return { 
 
\t \t "name": arr[0], 
 
     "birthday": { 
 
      "day": parseInt(arr[1]), 
 
      "month": parseInt(arr[2]), 
 
      "year": parseInt(arr[3]) 
 
     }, 
 
     "house": { 
 
      "type": arr[4], 
 
      "address": { 
 
       "street": arr[5], 
 
       "city": arr[6], 
 
       "state": arr[7] 
 
      }, 
 
      "occupants": parseInt(arr[8]) 
 
     } 
 
\t \t 
 
\t } 
 
}) 
 

 
console.log(output)

+0

這種方法也可以,但並不能解決問題;它仍然需要手動列出每個鍵。 – rapinopo

+0

嗯,是的,你這樣做了一次,然後如果你有一個500行的CSV,它會創建一個包含500個對象的數組。問題在哪裏? –