'How to create a nested data structure from 2 arrays?

Hi guys I get confused trying to qcheave this, so any help would be great!

I have two arrays from which I need a specific object. Source arrays:

1-Types (1D):    ['dev', 'env', 'othr']
2-Services (2D): (3) [Array(3), Array(4), Array(6)]
                 Array(3)
                   0: "'1':'Option 11'"
                   1: "'2':'Option 12'"
                   2: "'3':'Option 13'"

                 Array(4)
                   0: "'5':'Option 21'"
                   1: "'6':'Option 22'"
                   2: "'7':'Option 23'"
                   3: "'4':'Option 24'"

                 Array(6)
                   0: "'8':'Option 31'"
                   1: "'9':'Option 32'"
                   2: "'10':'Option 33'"
                   3: "'11':'Option 34'"
                   4: "'12':'Option 35'"
                   5: "'13':'Option 36'"

The desired outcome is this structure:

servData = {
  "dev": {
        "1": "Option 11",
        "2": "Option 12",
        "3": "Option 13"
  },
  "env": {
        "4": "Option 21",
        "5": "Option 22",
        "6": "Option 23",
        "7": "Option 24"
  },
  "othr": {
        "8": "Option 31",
        "9": "Option 32",
        "10": "Option 33",
        "11": "Option 34",
        "12": "Option 35",
        "13": "Option 36"
  }
};


Solution 1:[1]

Your data did need some cleanup. NOTE: Data has been updated and .map(a => a.replace(/'/g,'')) has been added to remove all '.

Use Array#map and Array#reduce as follows:

const keys = ['dev', 'env', 'othr'],
      input = [ [ "'1':'Option 11'", "'2':'Option 12'", "'3':'Option 13'" ], [ "'5':'Option 21'", "'6':'Option 22'", "'7':'Option 23'", "'4':'Option 24'" ], [ "'8':'Option 31'", "'9':'Option 32'", "'10':'Option 33'", "'11':'Option 34'", "'12':'Option 35'", "13:'Option 36'" ] ],
      
      output = keys.reduce(
          (o, key,i) => 
          ({
              ...o,
              [key]: input[i].map(a => a.replace(/'/g,'')).reduce(
                  (acc,cur) => 
                  ({...acc,[cur.split(':')[0]]:cur.split(':')[1]}),{})
          }),{}
      );
      
console.log( output );

In order to clean out the ' programmatically, especially the one after : use the following code:

const keys = ['dev', 'env', 'othr'],
      input = [ [ "'1':'Option 11'", "'2':'Option 12'", "'3':'Option 13'" ], [ "'5':'Option 21'", "'6':'Option 22'", "'7':'Option 23'", "'4':'Option 24'" ], [ "'8':'Option 31'", "'9':'Option 32'", "'10':'Option 33'", "'11':'Option 34'", "'12':'Option 35'", "'13':'Option 36'" ] ],
      
      output = input.map(s => s.map(e => e.replace(/'/g,'').split(':')))
      .reduce(
          (o, arr,i) => 
          ({...o, [keys[i]]: arr.reduce((acc,[k,v]) => ({...acc,[k]:v}), {})}),{}
      );
      
console.log( output );

Please note that, as pointed out by @PeterSeliger, replacing ' globally may have unintended consequences. For instance if you had a value such as "'14':'Option's Best'" would become "14:Options Best". So consider a more targetted approach. Maybe use of Function(...)() would avoid the need for replacing all the ''s. See the demo below:

const keys = ['dev', 'env', 'othr'],
      input = [ [ "'1':'Option 11'", "'2':'Option 12'", "'3':'Option 13'" ], [ "'5':'Option 21'", "'6':'Option 22'", "'7':'Option 23'", "'4':'Option 24'" ], [ "'8':'Option 31'", "'9':'Option 32'", "'10':'Option 33'", "'11':'Option 34'", "'12':'Option 35'", "'13':'Option 36'" ] ],
      
      output = input
      .reduce(
          (o, arr,i) => 
          ({
              ...o, 
              [keys[i]]: arr.reduce(
                  (acc,cur) => 
                  ({...acc,...Function(`return {${cur}}`)()}), {})
          }),{}
      );
      
console.log( output );

Solution 2:[2]

Use this:

const itemNames = ["dev", "env", "othr"];

const items = [
   ["'1':'Option 11'", "'2':'Option 12'", "'3':'Option 13'"],
   ["'5':'Option 21'", "'6':'Option 22'", "'7':'Option 23'", "'4':'Option 24'"],
   [
     "'8':'Option 31'",
     "'9':'Option 32'",
     "'10':'Option 33'",
     "'11':'Option 34'",
     "'12':'Option 35'",
     "'13':'Option 36'"
  ]
];
const value = itemNames
.map((value, index) => ({ index, value }))
.reduce((p, el) => {
  p[el.value] = {};

  items[el.index].forEach((e) => {
    let [op, ...va] = e.split(":");
    op = op.substring(1, op.length - 1);
    va = va.join(":");
    va = va.substring(1, va.length - 1);
    const ar = va;
    p[el.value][parseInt(op)] = ar;
  });

  return p;
}, {});

console.log(value);
.as-console-wrapper { min-height: 100%!important; top: 0; }

Solution 3:[3]

The OP's provided array of stringified entries seems to be mal-formatted ... e.g. "1':'Option 11'", with either a missing or a dispensable single quote for the key part, instead of "'1':'Option 11'" (with single quotes) or "1:'Option 11'" (without single quotes for the key part).

Because of that the beneath presented approach uses a capturing regex like /^'?(?<key>.*?)'?\s*:\s*'?(?<value>.*?)'?$/ in order to sanitize (or rather be agnostic to) each provided string based key-value configuration.

The approach itself makes use of Array.prototype.reduce twice.

The outer reduce does process the list of property or item names as entryKey together with a passed entries-config which features each property name's related array of stringified key-value pairs.

The inner reduce does process such an array, retrieves key and value from the string and, based on this data, assigns a new entry to the value which, within the outer reduce, gets assigned to the newly created entryKey related entry.

function createEntryFromKeyAndEntriesConfig(
  { config = [], result = {} },
  entryKey,
  idx,
) {
  // see ... [https://regex101.com/r/DQbrRM/3]
  const regXKeyValue =
    /^'?(?<key>.*?)'?\s*:\s*'?(?<value>.*?)'?$/;

  result[entryKey] = config[idx]
    .reduce((obj, entryString) => {

      const { key = '', value = '' } = regXKeyValue
        .exec(entryString)
        ?.groups;

      obj[key] = value;
      return obj;
    }, {});

  return { config, result };
}

const entriesConfig = [[
  "1':'Option 11'",
  "2':'Option 12'",
  "3':'Option 13'",
], [
  "5':'Option's 21st'", 
  "6':'Option\'s 22nd'", 
  "7':'Option's 23rd'", 
  "4':'Option\'s 24th'",
], [
  "8':'Option 31'", 
  "9':'Option 32'", 
  "10':'Option 33'", 
  "11':'Option 34'", 
  "12':'Option 35'", 
  "13':'Option 36'",
]];
const propertyNames = ['dev', 'env', 'othr'];

const { result: servData } = propertyNames
  .reduce(createEntryFromKeyAndEntriesConfig, {
    config: entriesConfig,
    result: {},
  });

console.log({ servData });
.as-console-wrapper { min-height: 100%!important; top: 0; }

Solution 4:[4]

Try this :

const arr1 = ['dev', 'env', 'othr'];

const arr2 = [["'1':'Option 11'", "'2':'Option 12'", "'3':'Option 13'"],
["'5':'Option 21'", "'6':'Option 22'", "'7':'Option 23'", "'4':'Option 24'"],
["'8':'Option 31'", "'9':'Option 32'", "'10':'Option 33'", "'11':'Option 34'", "'12':'Option 35'", "'13':'Option 36'"]];

const obj = {};

arr1.forEach((item, index) => {
  const innerObj = {};
  arr2[index].forEach(innerItem => {
    innerObj[innerItem.split(':')[0].replace(/['"]+/g, '')] = innerItem.split(':')[1].replace(/['"]+/g, '')
  });
    obj[item] = innerObj;
});

console.log(obj)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3
Solution 4 Rohìt Jíndal