1

I have a kind of complicated algorithm to write using Javascript.

I have a data structure like this:

const data = [
  {
    some_fees_dent_0: true,
    some_fees_name_0: "junao",
    some_fees_name_1: "adhm",
    some_fees_name_2: "uio",
    some_fees_rate_0: "45",
    some_fees_rate_1: "1",
    some_fees_rate_2: "22",
    some_fees: [],                # HERE
    initial_fees: [],             # HERE
    initial_fees_dent_0: true,
    initial_fees_name_0: "james",
    initial_fees_name_1: "daiep",
    initial_fees_name_2: "moaip",
    initial_fees_rate_0: "7",
    initial_fees_rate_1: "11",
    initial_fees_rate_2: "88",
    initial_fees_type_0: "foo",
    initial_fees_type_1: "bar",
    initial_fees_type_2: "random",
  }
]

I want to achieve this:

const data = [
  {
    some_fees: [
      {
        some_fees_name: "junao",  # was initially some_fees_name_0
        some_fees_rate: "45",     # was initially some_fees_rate_0
        some_fees_dent: true,     # was initially some_fees_dent_0
      },
      {
        some_fees_name: "adhm",   # was initially some_fees_name_1
        some_fees_rate: "1",      # was initially some_fees_rate_1
      },
      {
        some_fees_name: "uio",    # was initially some_fees_name_2
        some_fees_rate: "22",     # was initially some_fees_rate_2
      },

    ],
    initial_fees: [
      {
        initial_fees_name: "james",   # was initially initial_fees_name_0
        initial_fees_rate: "7",       # was initially initial_fees_rate_0
        initial_fees_type: "foo",     # was initially initial_fees_type_0
        initial_fees_dent: true,      # was initially initial_fees_dent_0
      },
      {
        initial_fees_name: "daiep",   # was initially initial_fees_name_1
        initial_fees_rate: "11",      # was initially initial_fees_rate_1
        initial_fees_type: "bar",     # was initially initial_fees_type_1
      },
      {
        initial_fees_name: "moaip",  # was initially initial_fees_name_2
        initial_fees_rate: "88",     # was initially initial_fees_rate_2
        initial_fees_type: "random", # was initially initial_fees_type_3
      },

    ],
  }
]

Basically what happened is the initial data array that include objects now only include only two fields: some_fees initial_fees.

The other fields were put together in the same object based on the last number on their key, Which can be considered as index differentiator.

John D
  • 285
  • 3
  • 22

2 Answers2

1

You must initialized parent first (some_fees, initial_fees) then group them by there pattern...

let data = [{ some_fees_dent_0: true, some_fees_name_0: "junao", some_fees_name_1: "adhm", some_fees_name_2: "uio", some_fees_rate_0: "45", some_fees_rate_1: "1", some_fees_rate_2: "22", some_fees: [], initial_fees: [], initial_fees_dent_0: true, initial_fees_name_0: "james", initial_fees_name_1: "daiep", initial_fees_name_2: "moaip", initial_fees_rate_0: "7", initial_fees_rate_1: "11", initial_fees_rate_2: "88", initial_fees_type_0: "foo", initial_fees_type_1: "bar", initial_fees_type_2: "random", }],
    groups = new Map,
    result = [];

for (let item of data) {
    let root = {}, entries = Object.entries(item);
    result.push(root);

    for (let [key, value] of entries)
        if (Array.isArray(value))
            root[key] = []; // initializing parent node

    for (let [key, value] of entries) {
        let [k0, k1, k2, k3] = key.split('_'),
            modefiedKey = `${k0}_${k1}_${k2}`,
            groupID = `${k0}_${k1}_${k3}`,
            group = groups.get(groupID);

        if (!k2) continue;
        if (group) group[modefiedKey] = value;
        else {
            let node = { [modefiedKey]: value }, parentNode = root[`${k0}_${k1}`];
            groups.set(groupID, node);
            if (parentNode) parentNode.push(node);
        }
    }
}
console.log(result);
Nur
  • 2,361
  • 2
  • 16
  • 34
  • thats impressive, im getting this error `The body of a for-in should be wrapped in an if statement to filter unwanted properties from the prototype` because of the use of `for (const key in item) {`, do you have a solution with an alternative and also last question...how would your remove `_number` for each key for the solution? – John D Apr 27 '21 at 17:43
  • is it syntax error? And explain the schema of your data structure... – Nur Apr 27 '21 at 17:44
  • *how would your remove _number for each key for the solution?* I didn't understand you quesion, Pls explain – Nur Apr 27 '21 at 17:47
  • I think its related to this article `https://stackoverflow.com/questions/1963102/what-does-the-jslint-error-body-of-a-for-in-should-be-wrapped-in-an-if-statemen` where the answer was `First of all, never use a for in loop to enumerate over an array.` `The reason behind this is the following: each object in JavaScript has a special field called prototype.` – John D Apr 27 '21 at 17:47
  • Sure, you see all the fields in the solution you provide? i want them to be `initial_fees_type: "random",` as an example for one of them instead of `initial_fees_type_2: "random",`. what happened is that i removed the suffix `_2`. I want the same thing for all of them. – John D Apr 27 '21 at 17:49
  • ooh I didn't modify anything... And you didn't specified that pattern in your question... how do i know that this is a thing? – Nur Apr 27 '21 at 17:52
  • Yes, sorry about that. I have updated my question with the correct expected solution data structure in the section `I want to achieve this:` It would be very helpful if you could help me solve that and the error caused by using a for loop...in for array. Thank you so much!!! – John D Apr 27 '21 at 18:00
  • Wait 2m, I will update my answer, And I don't use jslint... It just a linting error , – Nur Apr 27 '21 at 18:02
  • you can replace `_number`( regex: `/_\d/`) with empty string using regex. but you should use `${k0}_${k1}_${k2}` as key, Because `_number` could replace any part of key, like: `obj_prop_0type_0` would be `obj_proptype_0` – Nur Apr 27 '21 at 18:24
  • I see, thank you. any thought on the replacement of the `for loop..in` with something else? – John D Apr 27 '21 at 18:29
  • I use `for..in loop` for getting key from object (`data[0]`) you could use `for..of` loop, like `for( const [key,value] of Object.entries(item)) { ... }` I just update my answer, Happy hacking... – Nur Apr 27 '21 at 18:34
0

I was wondering if there might be some kind of more general solution to this sort of thing. The idea with create_object_from_structured_key_entries (which could probably be written a fair bit better) is that it'll take entries with 'structured' keys as per the example in the first console.log(), and then create an object with the appropriate structure. For your data there's a create_structured_key function to convert your keys to structured ones so that it'll run through create_object_from_structured_key_entries.

const data = [{"some_fees_dent_0":true,"some_fees_name_0":"junao","some_fees_name_1":"adhm","some_fees_name_2":"uio","some_fees_rate_0":"45","some_fees_rate_1":"1","some_fees_rate_2":"22","some_fees":[],"initial_fees":[],"initial_fees_dent_0":true,"initial_fees_name_0":"james","initial_fees_name_1":"daiep","initial_fees_name_2":"moaip","initial_fees_rate_0":"7","initial_fees_rate_1":"11","initial_fees_rate_2":"88","initial_fees_type_0":"foo","initial_fees_type_1":"bar","initial_fees_type_2":"random"}];

const create_object_from_structured_key_entries = (entries) =>
  entries.reduce(
    (acc, [key, val]) => {
      let current_object = acc;
      const dot_parts = key.split('.');
      
      dot_parts.forEach((dot_part, i) => {
        const arr_parts = dot_part.replace(/]/g, '').split('[');
        const for_array = arr_parts.length > 1;
        const array_index = for_array ? +arr_parts[1] : null;
        
        if(!current_object[arr_parts[0]])
          current_object[arr_parts[0]] = for_array ? [] : {};
        
        if(for_array && !current_object[arr_parts[0]][array_index])
          current_object[arr_parts[0]][array_index] = {};
        
        if(i === dot_parts.length - 1) {
          if(for_array) {
            current_object[arr_parts[0]][array_index] = val;
          } else {
            current_object[arr_parts[0]] = val;
          }
          return;
        }
        
        if(for_array) {
          current_object = current_object[arr_parts[0]][array_index];
        } else {
          current_object = current_object[arr_parts[0]];
        }
      });
      
      return acc;
    }, {}
  );

const create_structured_key = (key) => {
  const [k0, k1, k2, k3] = key.split('_');
  if(!k2) return null;
  return k0 + '_' + k1 + '[' + k3 + ']' + '.' + k0 + '_' + k1 + '_' + k2;
};

const data_item_to_structured_object = (item) =>
  create_object_from_structured_key_entries(
    Object.entries(item)
      .map(([key, val]) => [create_structured_key(key), val])
      .filter(([key, val]) => key)
  );
  
const data_to_structured_object = (arr) => arr.map(data_item_to_structured_object);

console.log(create_object_from_structured_key_entries( Object.entries({
  'a.aa[0].aaa[1]': 'bob',
  'a.aa[0].ddd': 'fred',
  'a.bb.eee': 'jane'
}) ));

console.log(data_to_structured_object(data));
Ben Stephens
  • 3,303
  • 1
  • 4
  • 8