1

The meta fields value is a an object that has been saved as a string I'm getting the data from mongo via mongoose with

const goodsIssued = await goods
  .find({ category: username, tokenName })
  .sort([['createdAt', -1]])
  .limit(2)
  .exec();

The data looks like..

{
    "goodsIssued": [
        {
            "minted": false,
            "_id": "5e3163597fd0ad2113bcdefe",
            "category": "gameco11",
            "goodId": 64,
            "issuedTo": "player2",
            "memo": "this is a test token",
            "meta": "{\"data\": \"text\", \"test\":1, \"works\": true}",
            "tokenName": "token5",
            "createdAt": "2020-01-29T10:50:01.257Z",
            "updatedAt": "2020-01-29T10:50:01.257Z",
            "__v": 0
        },
        {
            "minted": false,
            "_id": "5e3163587fd0ad2113bcdefd",
            "category": "gameco11",
            "goodId": 63,
            "issuedTo": "player2",
            "memo": "this is a test token",
            "meta": "{\"data\": \"text\", \"test\":1, \"works\": true}",
            "tokenName": "token5",
            "createdAt": "2020-01-29T10:50:00.691Z",
            "updatedAt": "2020-01-29T10:50:00.691Z",
            "__v": 0
        }
    ]
}

I want to convert it to an object before returning the array of objects back to the front end

for (const key in goodsIssued) {
  if (goodsIssued.hasOwnProperty(key)) {
    const parsedMeta = JSON.parse(goodsIssued[key].meta);
    goodsIssued[key].meta = parsedMeta;
  }
}

But it doesnt change? Why?

nopassport1
  • 1,821
  • 1
  • 25
  • 53
Bill
  • 4,614
  • 13
  • 77
  • 132
  • It does work for me. The original object has the meta properties parsed – Seblor Jan 29 '20 at 10:44
  • [Works for me](https://jsfiddle.net/tjcrowder/wgm2y4e0/). Note, though, that `for-in` isn't your best option for looping through an array. See [this question's answers](https://stackoverflow.com/questions/9329446/for-each-over-an-array-in-javascript) for more. Instead: `for (const entry of goodsIssued) { entry.meta = JSON.parse(entry.meta); }` – T.J. Crowder Jan 29 '20 at 10:46
  • 1
    You helped me refine the problem, I have updated the question – Bill Jan 29 '20 at 10:52
  • 1
    Why don't you store meta as an object instead of sring? – karaxuna Jan 29 '20 at 10:53
  • Actually, wait, the update does change something. :-) Your object is no longer an array, it's an object with a single property, goodsIssued, that's an array. Your code needs to work with that array rather than goodsIssued itself. (You might want to change the variable name to be less confusing.) – T.J. Crowder Jan 29 '20 at 11:15

1 Answers1

0

I see two possibilies, either of which could be the problem, or it may even b a combination of both.

  1. Your updated question shows the goodsIssued variable being assigned an object with a goodsIssued property which is an array, whereas your original question showed just the array. Your code is looping through that top-level object, not the array.

  2. I suspect you're getting the object from something that freezes the full object tree that it gives you. In that case, assigning to meta won't work because the object it's on is frozen.

If I'm right, in order to update those properties, you'll have to copy everything, perhaps like this:

// 1. Get the array, not the object containing it
let goodsIssued = (await /*...get the goods*/).goodsIssued;

// 2. Create a new, unfrozen array with objects with unfrozen properties,
// and parse `meta` along the way
goodsIssued = [...goodsIssued.map(entry => ({...entry, meta: JSON.parse(entry.meta)}))];

Live Example:

async function getTheGoods() {
    return Object.freeze({
        "goodsIssued": Object.freeze([
            Object.freeze({
                "minted": false,
                "_id": "5e3163597fd0ad2113bcdefe",
                "category": "gameco11",
                "goodId": 64,
                "issuedTo": "player2",
                "memo": "this is a test token",
                "meta": "{\"data\": \"text\", \"test\":1, \"works\": true}",
                "tokenName": "token5",
                "createdAt": "2020-01-29T10:50:01.257Z",
                "updatedAt": "2020-01-29T10:50:01.257Z",
                "__v": 0
            }),
            Object.freeze({
                "minted": false,
                "_id": "5e3163587fd0ad2113bcdefd",
                "category": "gameco11",
                "goodId": 63,
                "issuedTo": "player2",
                "memo": "this is a test token",
                "meta": "{\"data\": \"text\", \"test\":1, \"works\": true}",
                "tokenName": "token5",
                "createdAt": "2020-01-29T10:50:00.691Z",
                "updatedAt": "2020-01-29T10:50:00.691Z",
                "__v": 0
            })
        ])
    });
}

// (Your code is apparently in an `async` function)
(async () => {
    // 1. Get the array, not the object containing it
    let goodsIssued = (await getTheGoods()).goodsIssued;

    // 2. Create a new, unfrozen array with objects with unfrozen properties,
    // and parse `meta` along the way
    goodsIssued = [...goodsIssued.map(entry => ({...entry, meta: JSON.parse(entry.meta)}))];

    // Done!
    console.log(goodsIssued);
})()
.catch(error => {
    console.error(error);
});
.as-console-wrapper {
    max-height: 100% !important;
}

Or if you really want the wrapper object:

// 1. Get the object
let obj = await /*...get the goods...*/;

// 2. Create a new, unfrozen object with an unfrozen array with objects with unfrozen properties,
// and parse `meta` along the way
obj = {...obj, goodsIssued: [...obj.goodsIssued.map(entry => ({...entry, meta: JSON.parse(entry.meta)}))]};

Live Example:

async function getTheGoods() {
    return Object.freeze({
        "goodsIssued": Object.freeze([
            Object.freeze({
                "minted": false,
                "_id": "5e3163597fd0ad2113bcdefe",
                "category": "gameco11",
                "goodId": 64,
                "issuedTo": "player2",
                "memo": "this is a test token",
                "meta": "{\"data\": \"text\", \"test\":1, \"works\": true}",
                "tokenName": "token5",
                "createdAt": "2020-01-29T10:50:01.257Z",
                "updatedAt": "2020-01-29T10:50:01.257Z",
                "__v": 0
            }),
            Object.freeze({
                "minted": false,
                "_id": "5e3163587fd0ad2113bcdefd",
                "category": "gameco11",
                "goodId": 63,
                "issuedTo": "player2",
                "memo": "this is a test token",
                "meta": "{\"data\": \"text\", \"test\":1, \"works\": true}",
                "tokenName": "token5",
                "createdAt": "2020-01-29T10:50:00.691Z",
                "updatedAt": "2020-01-29T10:50:00.691Z",
                "__v": 0
            })
        ])
    });
}

// (Your code is apparently in an `async` function)
(async () => {
    // 1. Get the object
    let obj = await getTheGoods();

    // 2. Create a new, unfrozen object with an unfrozen array with objects with unfrozen properties,
    // and parse `meta` along the way
    obj = {...obj, goodsIssued: [...obj.goodsIssued.map(entry => ({...entry, meta: JSON.parse(entry.meta)}))]};

    // Done!
    console.log(obj);
})()
.catch(error => {
    console.error(error);
});
.as-console-wrapper {
    max-height: 100% !important;
}

Again, though, you may not need #1 or #2 (or you may indeed need both), depending on what the actual problem is.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875