0

First of all, i've read similar posts like this (here) but couldn't make it work somehow(:/), maybe i'm missing something. This is why i'm here. Basically i have a language translation file, and a json data that needs to be translated depend on the lang file. And this json data has nested objects which i need to iterate. Here are the file, data and code i have;

Translation file;

[
  {
    "Women": "Kadın"
  },    
  {
    "What's New": "Yeni Çıkanlar"
  },
  {
    "Tops": "Üstler"
  }    
]

Json data(i need to translate name values if there is a match in file);

{
"id": 2,
"parent_id": 1,
"name": "Default Category",
"is_active": true,
"position": 1,
"level": 1,
"product_count": 2072,
"children_data": [
    {
        "id": 20,
        "parent_id": 2,
        "name": "Women",
        "is_active": true,
        "position": 3,
        "level": 2,
        "product_count": 1012,
        "children_data": [
            {
                "id": 21,
                "parent_id": 20,
                "name": "Tops",
                "is_active": true,
                "position": 1,
                "level": 3,
                "product_count": 784,
                "children_data": [
                    {
                        "id": 23,
                        "parent_id": 21,
                        "name": "Jackets",
                        "is_active": true,
                        "position": 1,
                        "level": 4,
                        "product_count": 186,
                        "children_data": []
                    },
                    {
                        "id": 24,
                        "parent_id": 21,
                        "name": "Hoodies & Sweatshirts",
                        "is_active": true,
                        "position": 2,
                        "level": 4,
                        "product_count": 182,
                        "children_data": []
                    },
                    {
                        "id": 25,
                        "parent_id": 21,
                        "name": "Tees",
                        "is_active": true,
                        "position": 3,
                        "level": 4,
                        "product_count": 192,
                        "children_data": []
                    }
                ]
            }
        ]
    }
]}

What I Did; I created reqursive function that would map the object given for every key in the translation file, and iterate in every nested object in the nested data. But when i run it, looks like it goes infinitive loop and i dont get stack overflow, just keeps running(tryed to console log it just logs object constantly). I tryed to understand what's happening but depends on other posts it looks just fine. Somehow i couldn't iterate every children_data object in the data. So here is my code, and could really use some help. Thank you.

function reqursiveTranslate(langCode, p_object) {
//i send the whole data to this func in the first place and expect to get translated data
var categories = require('./lang/categories/' + langCode + '.json'); //the file
categories.forEach((cat) => {
    Object.entries(cat).map(([key, value]) => {
        if (p_object.name === key) {
            p_object.name = value //change the name with lang key
        }
        Object.keys(p_object).forEach(k => {

            if (typeof p_object[k] === 'object' && Object.keys(p_object[k]).length > 0) {
                //console.log('here');
                //console.log(p_object[k]);
                p_object[k] = reqursiveTranslate(langCode, p_object[k]);
            }
        })
    })
});
return p_object;
}

EDIT: With the help of @halilcakar found out above code works practically, but my problem is i get the data from another rest api and when it comes bigger i face with performance issues, take so long to get the translated data. So maybe a possible better approach on this would be usefull in order to reduce the cost of the reqursive function with bigger data.

Flardryn
  • 457
  • 1
  • 10
  • 25
  • Hi @Flardryn, I've created a small example with same code you gaved. It seems like it's working on my computer. – halilcakar May 02 '21 at 19:52
  • Here is a codepen link https://codepen.io/halilcakar/pen/vYgoXaV let me know if it's actually true :) – halilcakar May 02 '21 at 19:59
  • @halilcakar Thats strange, i couldn't get the result i want. My data and file are bigger than i posted(i get the data from another rest api). I'm going to simulate again. If thats so, my problem would be the performance issue. Do you think above code is the best solution for this? – Flardryn May 02 '21 at 20:00
  • Well actually it depends on what you use for. If it's a Vue.js app then you should just go and use i18n to make translates, I believe it's also working with react too. If this is only for translating and saving somewhere, I would go and examine the translation tools and see how they are doing it :) – halilcakar May 02 '21 at 20:07
  • @halilcakar Thanks for the advice unfortunately translation isn't the real goal here, need to do it with file key-value matches :) i got the result with smaller data now, i'll edit the post. Thanks for the comments and your time (çok teşekkürler:)) – Flardryn May 02 '21 at 20:12
  • Hope you can figure it out then, If I would be doing this, I would probably do traslation files like objects as key value peers and iterate over on the object (Since it's common practice to make translation files as key value peers instead of an array of objects =) (yardımcı olabildiysem ne mutlu :) – halilcakar May 02 '21 at 20:14

1 Answers1

0

Here is how you could solve this using object-scan. Downside is that you're added in a dependency. Upside is that it's somewhat maintainable

// const objectScan = require('object-scan');

const myData = { id: 2, parent_id: 1, name: 'Default Category', is_active: true, position: 1, level: 1, product_count: 2072, children_data: [{ id: 20, parent_id: 2, name: 'Women', is_active: true, position: 3, level: 2, product_count: 1012, children_data: [{ id: 21, parent_id: 20, name: 'Tops', is_active: true, position: 1, level: 3, product_count: 784, children_data: [{ id: 23, parent_id: 21, name: 'Jackets', is_active: true, position: 1, level: 4, product_count: 186, children_data: [] }, { id: 24, parent_id: 21, name: 'Hoodies & Sweatshirts', is_active: true, position: 2, level: 4, product_count: 182, children_data: [] }, { id: 25, parent_id: 21, name: 'Tees', is_active: true, position: 3, level: 4, product_count: 192, children_data: [] }] }] }] };

const myTranslation = [{ Women: 'Kadın' }, { "What's New": 'Yeni Çıkanlar' }, { Tops: 'Üstler' }];

const translate = (data, translation) => {
  const wordMap = new Map(objectScan(['[*].*'], { rtn: ['property', 'value'] })(translation));
  return objectScan(['**(^children_data$).name'], {
    useArraySelector: false,
    rtn: 'count',
    filterFn: ({ parent, property, value }) => {
      if (wordMap.has(value)) {
        parent[property] = wordMap.get(value);
        return true;
      }
      return false;
    }
  })(data);
};

console.log(translate(myData, myTranslation));
// => 2

console.log(myData);
// => { id: 2, parent_id: 1, name: 'Default Category', is_active: true, position: 1, level: 1, product_count: 2072, children_data: [ { id: 20, parent_id: 2, name: 'Kadın', is_active: true, position: 3, level: 2, product_count: 1012, children_data: [ { id: 21, parent_id: 20, name: 'Üstler', is_active: true, position: 1, level: 3, product_count: 784, children_data: [ { id: 23, parent_id: 21, name: 'Jackets', is_active: true, position: 1, level: 4, product_count: 186, children_data: [] }, { id: 24, parent_id: 21, name: 'Hoodies & Sweatshirts', is_active: true, position: 2, level: 4, product_count: 182, children_data: [] }, { id: 25, parent_id: 21, name: 'Tees', is_active: true, position: 3, level: 4, product_count: 192, children_data: [] } ] } ] } ] }
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan@14.3.0"></script>

Disclaimer: I'm the author of object-scan

vincent
  • 1,953
  • 3
  • 18
  • 24
  • 1
    Sorry for the late reply, had things to do. I've tryed it and time cost is amazing compare to my reqursive function. Thank you :) – Flardryn May 09 '21 at 20:47
  • Hi again, i'm tryng `object-scan` for the same data again but have another problem :) i want to get the id's of objects which have no children_data. I've tryed `objectScan(['**(^children_data$)']` and tryed the `fn` like `filterFn: ({ value }) => value === undefined` and set the `rtn` : parent. But keep getting the undefined return. Do u have any example for it that i could inspect? @vincent – Flardryn May 10 '21 at 18:23
  • When you say "no children data", doe you mean they have an empty array or undefine as "children_data"? You could do `objectScan(['**(^children_data$)'], { filterFn: ({isLeaf, value}) => !isLeaf && !('children_data' in value || value.children_data.length === 0), rtn: 'value' })(...)` – vincent May 10 '21 at 18:44
  • Yes i meant empty array :) This is not the exact think i'm looking for. This doesn't go deep, and i had to cut value.children_data part bec of the error. Should i create another post for it maybe with code snippet? (Im tryng combinations by the way but cant get it yet) – Flardryn May 10 '21 at 18:53
  • 1
    Oh i think i've made it. I added `useArraySelector: false`. Before this it was returning the first level :) Got what i want thank u so much again :):) – Flardryn May 10 '21 at 19:09
  • 1
    Sorry, forgot to add that. Glad you got it working! – vincent May 10 '21 at 19:10