0

I'm storing key-value pairs as plaintext:

headerlessKey=blahblahblah
{partName}
keyName=value

If I put null/undefined as first parameter the key should not have a parent part:

DB.write(null,'headerlessKey','hello world')
DB.write('stuff','someOtherKey',1)

data.tdb:

headerlessKey=hello world
{stuff}
someOtherKey=1

But instead the file looks like:

{stuff}
someOtherKey=1
{null}
headerlessKey=hello world

I want headerlessKey at top of file contents with no {null} part. I know:

  1. If I change the value being written while there is still stuff in data.tdb the {null} part appears.

  2. Order of operations doesn't matter:

    DB.write(null,'headerlessKey','hello world')
    DB.write('stuff','someOtherKey',1)
    

    Or:

    DB.write('stuff','someOtherKey',1)
    DB.write(null,'headerlessKey','hello world')
    

    {null} will still appear.

  3. .tdb file has no {null} part without concurrent DB.write() function calls.

  4. Issue is likely in ._saveDataToFile().

DB.js:

import fs from 'fs';

class DB {
    constructor(filePath) {
        this.filePath = filePath;
        this.data = this._loadDataFromFile();
    }

    _loadDataFromFile() {
        try {
            const data = fs.readFileSync(this.filePath, 'utf8');
            const entries = data.split(/\r?\n/); // Split by lines
            const parsedData = {};
            let currentPart = null; // Initialize with null

            for (const entry of entries) {
                if (entry.startsWith('{')) {
                    currentPart = entry.slice(1, -1);
                    parsedData[currentPart] = {};
                } else if (entry.includes('=')) {
                    const [key, value] = entry.split('=');
                    if (currentPart !== null && key) {
                        parsedData[currentPart][key] = value;
                    }
                }
            }
            return parsedData;
        } catch (error) {
            return {};
        }
    }

    _saveDataToFile() {
        let dataStr = '';
        for (const part in this.data) {
            if (part !== null) {
                dataStr += `{${part}}\n`;
                for (const key in this.data[part]) {
                    dataStr += `${key}=${this.data[part][key]}\n`;
                }
            }
        }
        if (dataStr.startsWith('{null}\n')) {
            dataStr = dataStr.substring('{null}\n'.length);
        }
        fs.writeFileSync(this.filePath, dataStr, 'utf8');
    }

    // Public method to read a value
    read(partName, keyName) {
        if (this.data[partName] && this.data[partName][keyName]) {
            return this.data[partName][keyName];
        }
        return undefined;
    }

    // Public method to write a value
    write(partName, keyName, value) {
        if (!this.data[partName]) {
            this.data[partName] = {};
        }
        this.data[partName][keyName] = value;
        this._saveDataToFile();
        return value;
    }
}

export default DB;

I tried a case in the for loop, but it didn't work. I tried .substring() and that failed.

user4157124
  • 2,809
  • 13
  • 27
  • 42
  • Please ask 1 specific researched non-duplicate question. Please either ask re 1 bad query/function with obligatory [mre], including why you think it should return something else or are unsure at the 1st subexpression that you get what you expect or are stuck, justified by reference to authoritative documentation, or ask about your overall goal giving working parts you can do with justification & a [mre--then misunderstood code doesn't belong. But please ask about unexpected behaviour 1st because misconceptions get in the way of your goal. [ask] [Help] Basic questions are faqs. – philipxy Aug 26 '23 at 19:10
  • Debug questions require a [mre]--cut & paste & runnable code including initialization; desired & actual output (including verbatim error messages); tags & versions; clear specification & explanation. For debug that includes the least code you can give that is code that you show is OK extended by code that you show is not OK. [ask] [Help] When you get a result you don't expect, find the first point in the execution where the value of a variable or subexpression is not what you expect & say what you expected & why, justified by documentation. (Debugging fundamental.) – philipxy Aug 26 '23 at 19:11
  • 1
    There are a hundred different packages for writing Javascript objects to file. So, go file a bug report against this one and use another one. – Tim Roberts Aug 27 '23 at 00:08

1 Answers1

0

Because for (const part in this.data) will iterate the keys of this.data as strings. So even if you do this.data[null] = {...} in your loop, part won't be null but "null" and thus if (part !== null) will always be false ...

var x = {}
x[null] = "foobar";

console.log(x[null] === x["null"]);

for (let key in x) {
  console.log(key, typeof key, key === null, key === "null")
}

Furthermore, even if your check for null worked, you wouldn't get your expected result. Because if it worked, the content of key null won't show up in the result at all ...

And finally, your replacement won't work, because {null} is somewhere in the middle of the dataStr (as can be seen in the output) and thus dataStr.startsWith('{null}\n') obviously will return false and no replacement will be done ...

So for your dedired result do as follows

_saveDataToFile() {
    let dataStr = '';
    // ensure the null data is written at the top, if it exists 
    if (this.data[null]) {
        for (const key in this.data[null]) {
            dataStr += `${key}=${this.data[null][key]}\n`;
        }
    }

    for (const part in this.data) {
        if (part !== "null") { //ignore null data here
            dataStr += `{${part}}\n`;
            for (const key in this.data[part]) {
                dataStr += `${key}=${this.data[part][key]}\n`;
            }
        }
    }
    fs.writeFileSync(this.filePath, dataStr, 'utf8');
}
derpirscher
  • 14,418
  • 3
  • 18
  • 35