Looping throug nested object - map, reduce

Hello, could anyone help to clean this data - remove quotations around objects and arrays (by using JSON parser)? Ideally recursively, but 1 level deep is also fine for me.

const data = [
    {
        "post_id":8,
        "type":1,
        "author":"Marta",
        "categories":"[\"ludzie\"]",
        "tags":"[\"accesories\",\"handcraft\"]",
        "featured_img":"{\"file\": \"1.jpg\", \"2x\": null}"
    },
    {
        "post_id":7,
        "type":1,
        "author":"Marta",
        "categories":"[\"ludzie\"]",
        "tags":"[\"accesories\",\"handcraft\"]",
        "featured_img":"{\"file\": \"IMG_1293-2.jpg\", \"2x\": null}"
    }
];

What’s the point of removing the quotes? Are you trying to get a string that looks like this?

"tags": "[accesories, handcraft]"

It seems bizarre to have data that looks like the above. What are you trying to achieve?

Also, what do you by recursively? Are you saying you need to handle nested objects that look like this?

{
    "featured_img":"{\"file\": \"1.jpg\", \"2x\": null}",
    "foo": {
        "bar": "{\"file\": \"1.jpg\", \"2x\": null}"
    }
}

I don’t know where you get the data like that but unless you can’t control the backend, this kind of stuff should be handled in your backend.

Anyway, this is the code for that

const data = [
    {
        "post_id":8,
        "type":1,
        "author":"Marta",
        "categories":"[\"ludzie\"]",
        "tags":"[\"accesories\",\"handcraft\"]",
        "featured_img":"{\"file\": \"1.jpg\", \"2x\": null}"
    },
    {
        "post_id":7,
        "type":1,
        "author":"Marta",
        "categories":"[\"ludzie\"]",
        "tags":"[\"accesories\",\"handcraft\"]",
        "featured_img":"{\"file\": \"IMG_1293-2.jpg\", \"2x\": null}"
    }
];

const removeLiteralQuotes = (o) => Object.entries(o).reduce((result, [key, val]) => {
  if (typeof val === 'string') {
  	result[key] = val.replace(/"/g, '');
  } else {
  	result[key] = val;
  }
  return result;
}, {})

console.log(data.map(removeLiteralQuotes))

Hi, please let me explain the problem and give you more details.
The output of Mariadb (slightly extended - in comparison to my previous example) is as below. All objects values (both arrays and objects) are surrounded by “”/’’. This is really problematic as I finally need Json object in order to easily inject appropriate data into the template. Please have a look at categories, tags, featured_img and images (here nested object) keys. They should look like as at the bottom (I also removed escape characters in order to visualize that). Why recursively? - because I don’t know prior the data I would have need (key names, how deep the object is).

Mriadb

const data = [
    {
        "post_id":8,
        "type":1,
        "author":"Marta",
        "categories":"[\"ludzie\"]",
        "tags":"[\"accesories\",\"handcraft\"]",
        "featured_img":"{\"file\": \"1.jpg\", \"2x\": null}",
        "images":'["{\\"file\\": \\"Fiolet i zieleń.png\\", \\"2x\\": null, \\"3x\\": null, \\"pinIt\\": false, \\"alt\\": \\"Piękny obraz\\", \\"figcaption\\": null}","{\\"file\\": \\"Moja Japonia.jpg\\", \\"2x\\": null, \\"3x\\": null, \\"pinIt\\": true, \\"alt\\": \\"aaa\\", \\"figcaption\\": null}"]'
    },
    {
        "post_id":7,
        "type":1,
        "author":"Marta",
        "categories":"[\"ludzie\"]",
        "tags":"[\"accesories\",\"handcraft\"]",
        "featured_img":"{\"file\": \"IMG_1293-2.jpg\", \"2x\": null}",
        "images":'["{\\"file\\": \\"Fiolet i zieleń.png\\", \\"2x\\": null, \\"3x\\": null, \\"pinIt\\": false, \\"alt\\": \\"Piękny obraz\\", \\"figcaption\\": null}","{\\"file\\": \\"Moja Japonia.jpg\\", \\"2x\\": null, \\"3x\\": null, \\"pinIt\\": true, \\"alt\\": \\"aaa\\", \\"figcaption\\": null}"]'
    }
];

expected result

const data = [
    {
        ....
        "categories":["ludzie"],
        "tags":["accesories","handcraft"],
        "featured_img":{"file": "1.jpg", "2x": null},
        "images":[{"file": "Fiolet i zieleń.png", "2x": null, "3x": null, "pinIt": false, "alt": "Piękny obraz", "figcaption": null},{"file": "Moja Japonia.jpg", "2x": null, "3x": null, "pinIt": true, "alt": "aaa", "figcaption": null}]
    },
    {
        ....
    }
];

Okay, so you have access to the backend. Then, how do you end up getting results like that in the first place?

Since you have an array of objects in Javascript, you must have used JSON.parse. If so, can you show me the string that was passed to JSON.parse?

You don’t want to handle this on your client side. There must be something that you are doing wrong in your backend when encoding data to JSON.

Seems everything is ok when it comes to beck-end. Here is part of my query and a screen how Json looks like (not 1:1 comparison to data - but you’ve got the point).

BTW - from Mariadb documentation: In MariaDB JSON strings are normal strings and compared as strings

    const {lang, slug} = req.params;
    const sql_postBySlug = `CALL get_post_by_slug_3(?,?)`;
    const [post_res, status_d] = await db.query(sql_postBySlug, [lang, slug]);
    res.json(post_res[0]);

Sigh… no matter how much you love JSON type. It will only create more problems for you like this one…
If I were you I’d just use SQL the good old-fashioned way with a library like knex.js if I didn’t want to learn fully-featured ORM.

Anyway, here’s a duck tape solution for your problem.

function parseJSONProps(obj) {
  return Object.entries(obj).reduce((result, [key, val]) => {
    switch (typeof val) {
      case 'string':
        try {
          result[key] = JSON.parse(val)
        } catch(e) {
          result[key] = val
        }
        break
      case 'object':
        result[key] = parseJSONProps(val)
        break
      default:
        result[key] = val
        break
    }
    return result
  }, {})
}

const d = parseJSONProps({
  "post_id":8,
  "type":1,
  "author":"Marta",
  "categories":"[\"ludzie\"]",
  "tags":"[\"accesories\",\"handcraft\"]",
  "featured_img":"{\"file\": \"1.jpg\", \"2x\": null}",
  "nested": {
    "post_id":8,
    "type":1,
    "author":"Marta",
    "categories":"[\"ludzie\"]",
    "tags":"[\"accesories\",\"handcraft\"]",
    "featured_img":"{\"file\": \"1.jpg\", \"2x\": null}",
    "nested": {
      "post_id":8,
      "type":1,
      "author":"Marta",
      "categories":"[\"ludzie\"]",
      "tags":"[\"accesories\",\"handcraft\"]",
      "featured_img":"{\"file\": \"1.jpg\", \"2x\": null}"
    }
  }
})

console.log(d.nested.nested.tags)

It assumes the JSON string is in a valid format and has some corner cases. But it’s probably good for your use case.

Yeah, more problems than I wish to have. Thank you, it works well 1 level deep, but unfortunately I have objects inside an array (“images”) - in your case (“nested”). Could we also remove quotations here somehow?

"images":'["{\\"file\\": \\"Fiolet i zieleń.png\\", \\"figcaption\\": null}",
           "{\\"file\\": \\"Moja Japonia.jpg\\", \\"figcaption\\": null}"]'

At that point, you are just being unreasonable to keep marching on your crusade.
Frankly, this ain’t kind of crusade I want to join.

You should really rethink how you design your query. If I remember correctly you already have normalized tables and those JSON types are just coming from the view. This means you are creating problems yourself. You currently don’t have a heterogeneous schema, so why bother using JSON?

Anyway, this hasn’t been thoroughly tested but works for your case.

function unstringify(val) {
  try {
    const parsedValue = JSON.parse(val)
    switch(true) {
      case Array.isArray(parsedValue):
        return parsedValue.map(unstringify)
      case typeof parsedValue === 'object':
        return Object.entries(parsedValue).reduce((o, [key, val]) => {
          o[key] = unstringify(val)
          return o
        }, {})
      default:
        return parsedValue
    }
  } catch(e) {
    return val;
  }
}

function parse(obj) {
  return Object.entries(obj).reduce((o, [key, val]) => {
    o[key] = unstringify(val)
    return o
  }, {})
}

console.log('TEST unstringify')
const test_data = JSON.stringify({
  str: 'hello',
  num: 1,
  bool: true,
  arr: JSON.stringify([1, 2]),
  arr_arr: JSON.stringify([
    JSON.stringify([1, 2]),
    JSON.stringify([1, 2])
  ]),
  obj: JSON.stringify({ a: 1, b: 2 }),
  obj_with_arr: JSON.stringify({
    a: JSON.stringify([1, 2])
  }),
  arr_objs: JSON.stringify([
    JSON.stringify({ a: 1, b: 2 }),
    JSON.stringify({ a: 1, b: 2 })
  ]),
  arr_obj_with_arr: JSON.stringify([
    JSON.stringify({
      a: JSON.stringify([1, 2])
    })
  ]),
  arr_obj_with_arr_obj: JSON.stringify([
    JSON.stringify({
      a: JSON.stringify([
        JSON.stringify({ a: 1, b: 2 }),
        JSON.stringify({ a: 1, b: 2 })
      ])
    })
  ])
})
console.log(unstringify(test_data).arr_obj_with_arr_obj[0])

console.log('\nTest parse')
const your_data = [
  {
    "post_id":8,
    "type":1,
    "author":"Marta",
    "categories":"[\"ludzie\"]",
    "tags":"[\"accesories\",\"handcraft\"]",
    "featured_img":"{\"file\": \"1.jpg\", \"2x\": null}",
    "images":'["{\\"file\\": \\"Fiolet i zieleń.png\\", \\"2x\\": null, \\"3x\\": null, \\"pinIt\\": false, \\"alt\\": \\"Piękny obraz\\", \\"figcaption\\": null}","{\\"file\\": \\"Moja Japonia.jpg\\", \\"2x\\": null, \\"3x\\": null, \\"pinIt\\": true, \\"alt\\": \\"aaa\\", \\"figcaption\\": null}"]',
  },
  {
    "post_id":8,
    "type":1,
    "author":"Marta",
    "categories":"[\"ludzie\"]",
    "tags":"[\"accesories\",\"handcraft\"]",
    "featured_img":"{\"file\": \"1.jpg\", \"2x\": null}",
    "images":'["{\\"file\\": \\"Fiolet i zieleń.png\\", \\"2x\\": null, \\"3x\\": null, \\"pinIt\\": false, \\"alt\\": \\"Piękny obraz\\", \\"figcaption\\": null}","{\\"file\\": \\"Moja Japonia.jpg\\", \\"2x\\": null, \\"3x\\": null, \\"pinIt\\": true, \\"alt\\": \\"aaa\\", \\"figcaption\\": null}"]',
  }
]
const parsed = your_data.map(parse)
console.log(parsed)
console.log(parsed[0].images)
2 Likes

Thank you, I really appreciate your time and effort. Will be able to test it later.

Well, I’m trying to be organized thus such query. Item position inside an array defines its order, for many items where orders doesn’t matter I’m using objects. I not sure whether my approach is correct in fact - I don’t have to much experience it that, especially that ~100% courses recommend and rely on Mongodb.

I think you would benefit a lot from taking any modern courses that build a full-stack app.
Without fundamentals and guidance, it is very easy to head to the road of friction and get lost in there. Knowing when to stop and reevaluate your approach is also valuable.

I might be wrong because I’m not that experienced either. But I can at least tell your approach deviates a lot from the norm.

Anyway, good luck with your journey.

Works, thank you. I have the last question on how to implement it into Express and integrate them as now they generates [Object Object]

const {unstringify, parse} = require("../utils/common")

router.get("/qwerty", (req, res, next) => {

    const data = [
        {"images":'["{\\"file\\": \\"Fiolet i zieleń.png\\", \\"2x\\": null, \\"3x\\": null, \\"pinIt\\": false, \\"alt\\": \\"Piękny obraz\\", \\"figcaption\\": null}","{\\"file\\": \\"Moja Japonia.jpg\\", \\"2x\\": null, \\"3x\\": null, \\"pinIt\\": true, \\"alt\\": \\"aaa\\", \\"figcaption\\": null}"]'},
        {"images":'["{\\"file\\": \\"Fiolet i zieleń.png\\", \\"2x\\": null, \\"3x\\": null, \\"pinIt\\": false, \\"alt\\": \\"Piękny obraz\\", \\"figcaption\\": null}","{\\"file\\": \\"Moja Japonia.jpg\\", \\"2x\\": null, \\"3x\\": null, \\"pinIt\\": true, \\"alt\\": \\"aaa\\", \\"figcaption\\": null}"]'}
    ];

    const post = data.map(parse)

    console.log(post)
    res.send(`<h1>${out}</h1>`)
})

out

  {
    post_id: 8,
    type: 1,
    author: 'Marta',
    categories: [ 'ludzie' ],
    tags: [ 'accesories', 'handcraft' ],
    featured_img: { file: '1.jpg', '2x': null },
    images: [ [Object], [Object] ]
  },
  {
    post_id: 7,
    type: 1,
    author: 'Marta',
    categories: [ 'ludzie' ],
    tags: [ 'accesories', 'handcraft' ],
    featured_img: { file: 'IMG_1293-2.jpg', '2x': null },
    images: [ [Object], [Object] ]
  }
]

It’s unclear what you are trying to do. That’s what you get when you toString() objects. If you want to see the JSON representation, use res.json. I’m not familiar with Express, so you need to check the doc.

Right, no console.log