3

I try to load contents of the CSV file into the array of JavaScript objects. Unfortunately, structure of data is not flat, so to mitigate that, I use dot-separated property names as column headers in CSV file. Example CSV file looks as follows:

codes.code1,codes.code2,codes.code3,codes.code4,info.description.text,info.description.language
49074202,64,1443,1416,Test description: 49074202 64 1443 1416,EN
81905948,10,9721,5411,Test description: 81905948 10 9721 5411,EN
87262350,86,7050,4775,Test description: 87262350 86 7050 4775,EN

The object structure I would like to end up with looks like

{
    codes: {
        code1: "49074202",
        code2: "64",
        code3: "1443",
        code4: "1416"
    },
    info: {
        description: {
            text: "Test description: 49074202 64 1443 1416",
            language: "EN"
        }
    }
}

Following code can be used to recreate the problem (using CSV file with contents posted above).

<html>
    <body>
        <form>
            <input type="file" id="files" onchange="loadFromFile()" />
            <script>
                function loadFromFile() {
                    var selectedFile = document.getElementById('files').files[0];
                    var reader = new FileReader();
                    reader.onload = loadRowsFromFile;
                    reader.readAsText(selectedFile);
                };

                function loadRowsFromFile(e) {
                    var rows = e.target.result.split("\n");
                    if (!rows || rows.length === 0) {
                        return;
                    }
                    var headers = rows[0].split(",");

                    var loadedData = [];
                    for (var i = 1; i < rows.length; i++) {
                        var columns = rows[i].split(",");

                        var rowData = {};
                        for (var j = 0; j < headers.length; j++) {
                            placeElementInHierarchy(rowData, headers[j], columns[j]);
                        }
                        loadedData.push(rowData);
                    }
                    console.log(loadedData);
                }

                function placeElementInHierarchy(rowData, propertyPath, value) {
                    var path = propertyPath.split(".");
                    var obj = rowData;
                    for (var i = 0; i < path.length; i++) {
                        if (i === path.length - 1) {
                            obj[path[i]] = value;
                        } else {
                            if (!obj[path[i]]) {
                                obj[path[i]] = {};
                            }
                            obj = obj[path[i]];
                        }
                    }
                }
            </script>
        </form>
    </body>
</html>

File parsing works properly, data is loaded correctly into the structure. However, for some reason the last processed property name is quoted. Structure of all rows I obtain looks like that:

{
    codes: {
        code1: "49074202",
        code2: "64",
        code3: "1443",
        code4: "1416"
    },
    info: {
        description:{
            text: "Test description: 49074202 64 1443 1416",
            "language": "EN"
        }
    }
}

The only lead I have right now is that if I remove the 'language' column from CSV file, then 'text' is being quoted instead - so the last processed property is for some reason quoted. Thanks in advance!

EDIT: Fixed mistakes in the expected structure.

EDIT2: What is interesting, following code (skipping file upload part), doesn't show such symptoms

<html>
    <body>
        <form>
            <script>                


                (function() {
                    var csvData = 'codes.code1,codes.code2,codes.code3,codes.code4,info.description.text,info.description.language\n' +
                                '49074202,64,1443,1416,Test description: 49074202 64 1443 1416,EN\n' +
                                '81905948,10,9721,5411,Test description: 81905948 10 9721 5411,EN\n' +
                                '87262350,86,7050,4775,Test description: 87262350 86 7050 4775,EN';
                    loadRowsFromFile(csvData);
                })();



                function loadRowsFromFile(csvData) {
                    var rows = csvData.split("\n");
                    if (!rows || rows.length === 0) {
                        return;
                    }
                    var headers = rows[0].split(",");

                    var loadedData = [];
                    for (var i = 1; i < rows.length; i++) {
                        var columns = rows[i].split(",");

                        var rowData = {};
                        for (var j = 0; j < headers.length; j++) {
                            placeElementInHierarchy(rowData, headers[j], columns[j]);
                        }
                        loadedData.push(rowData);
                    }
                    console.log(loadedData);
                }

                function placeElementInHierarchy(rowData, propertyPath, value) {
                    var path = propertyPath.split(".");
                    var obj = rowData;
                    for (var i = 0; i < path.length; i++) {
                        if (i === path.length - 1) {
                            obj[path[i]] = value;
                        } else {
                            if (!obj[path[i]]) {
                                obj[path[i]] = {};
                            }
                            obj = obj[path[i]];
                        }
                    }
                }
            </script>
        </form>
    </body>
</html>
darklurker
  • 33
  • 4
  • The quotes are not introduced by your code. – Pointy Jun 27 '18 at 12:58
  • Also it's not clear how the "info" column headers would generate the structure you posted. You've got `info.description.text` and `info.description.language`, so it seems like you should end up with an "info" property that has a single "description" property, which in turn has two properties, "text" and "language". – Pointy Jun 27 '18 at 13:01
  • Anyway the quotes don't matter. Add `console.log(loadedData[0].info.description.language)` and you'll get "EN" I predict. – Pointy Jun 27 '18 at 13:04
  • Fixed mistakes in expected output - sorry for that. Quotes seem to make a difference, when I later try to access this data using Angular directives. – darklurker Jun 27 '18 at 13:07
  • I think the quotes are an artifact introduced by `console.log()`. Try it in a different browser. – Pointy Jun 27 '18 at 13:08
  • This is not related to console.log(). Tested using development tools in Firefox & Chrome, as well as actual production code that depends on this script. Problem is with the internal structure of produced JS objects. – darklurker Jun 27 '18 at 13:10
  • The thing is, there are no `"` characters introduced anywhere by your code. – Pointy Jun 27 '18 at 13:11
  • Well, this is pretty much what this question is about - where these quotations come from? – darklurker Jun 27 '18 at 13:13
  • 1
    Can you please put together a [mcve], maybe using Stack Snippets (icon in editor looks like `<>`)? You'll likely want to store the CSV in a variable and change your code accordingly. – Heretic Monkey Jun 27 '18 at 13:26
  • With very minor changes (basically as @MikeMcCaughan described), your code works perfectly well when I run it in Node. One thing I notice is that your `loadRowsFromFile()` function does not include a `return` statement, so it ultimately does not really do anything useful. How exactly did you conclude that the mysterious quotes are causing the actual problem you're experiencing? – Pointy Jun 27 '18 at 13:54
  • I've extracted the upload part from the larger project to decrease amount of code. Normally loadRowsFromFile stores the results in one of controller's properties. – darklurker Jun 27 '18 at 14:15

1 Answers1

1

enter image description here Okay. As you can see in screen above, your problem is generated by Carriage Return character \r on end of every line in the file. You can remove all your empty characters from string using

trim()

or you can use regexp to cut out just this type of characters. Example is done here: How to remove all line breaks from a string?

Grachol
  • 26
  • 4