Ankit Agarwal
Ankit Agarwal

Reputation: 30739

Create an object with required tree structure

I have been trying to create a tree structure as:

var result = {
  email: {
    schema: {
      verified: 'email.verified',
      email_address: 'email.email_address',
      entity: {
        schema: {
          name: 'email.entity.name',
          type: 'email.entity.type',
          email: {
            schema: {
              verified: 'email.entity.email.verified',
              email_address: 'email.entity.email.email_address'
            }
          }
        }
      }
    }
  }
};

From the array data as given below:

var schema = {
  'paths': [
    'email.email_address',
    'email.entity.email.email_address',
    'email.entity.email.verified',
    'email.entity.type',
    'email.entity.name',
    'email.verified',
    'created_at',
    'contact_numbers'
  ]
}

You see that the output I expect has schema property nested withing n levels and that is not predictable. It depends on the value of the paths like as in 'email.entity.email.email_address'. You can split that with dot and seemingly each dot is replaced with schema property in the output.

I am trying to use recursion, but I am not able to set that flow. Below is the code I have tried so far:

var schema = {
  'paths': [
    'email.email_address',
    'email.entity.email.email_address',
    'email.entity.email.verified',
    'email.entity.type',
    'email.entity.name',
    'email.verified',
    'created_at',
    'contact_numbers'
  ]
}
var newSchema = {};

var key = 'email';
var existKeys = schema.paths.filter((path) => path.includes(key + '.'));
var requiredObject = {};
existKeys.forEach((existKey) => {
  var splitKeys = existKey.split('.');
  splitKeys.forEach((splitKey, index) => {
    if (requiredObject[splitKey] && index + 1 === splitKeys.length) {
      requiredObject[splitKey].schema = existKey;
    } else {
      requiredObject[splitKey] = {
        'schema': {}
      }
    }
  });
});
console.log(requiredObject);

Upvotes: 1

Views: 108

Answers (3)

Nitish Narang
Nitish Narang

Reputation: 4184

Using 2 Array.reduce you can achieve like below

var schema = {
  'paths': [
    'email.email_address',
    'email.entity.email.email_address',
    'email.entity.email.verified',
    'email.entity.type',
    'email.entity.name',
    'email.verified',
    'created_at',
    'contact_numbers'
  ]
}

let res = schema.paths.reduce((o, d) => {
  let keys = d.split('.')
  keys.reduce((t, k, i) => {
    t[k] = (i != keys.length - 1)
              ? (t[k] || { schema: {} })
              : d

    return t[k].schema
  }, o)
  
  return o
}, {})

console.log(res)

// for only email
console.log({ email: res.email })

Upvotes: 0

CertainPerformance
CertainPerformance

Reputation: 370619

I'd reduce over the paths array, using a helper function assignNested which, given an outer object, a value, and a property array, uses the top property to create the schema object and recursively call itself, until the array has only one item left, and the final value can be assigned:

var schema = {
  'paths': [
    'email.email_address',
    'email.entity.email.email_address',
    'email.entity.email.verified',
    'email.entity.type',
    'email.entity.name',
    'email.verified',
    'created_at',
    'contact_numbers'
  ]
};

function assignNested(obj, val, props) {
  if (props.length === 1) {
    obj[props[0]] = val;
    return;
  }
  const nextProp = props.shift();
  if (!obj.schema) obj.schema = {};
  if (!obj.schema[nextProp]) obj.schema[nextProp] = {};
  assignNested(obj.schema[nextProp], val, props);
}

const fullResult = schema.paths.reduce((a, path) => {
  const props = path.split('.');
  assignNested(a, path, props);
  return a;
}, {});

// fullResult contains the *full* structure,
// but if you only want the nested `email` part, then:
const result = {
  email: {
    schema: fullResult.schema.email
  }
};
console.log(result);

Upvotes: 0

Nina Scholz
Nina Scholz

Reputation: 386519

You could reduce the paths and take for every found key a schema property as well.

var schema = { paths: ['email.email_address', 'email.entity.email.email_address', 'email.entity.email.verified', 'email.entity.type', 'email.entity.name', 'email.verified', 'created_at', 'contact_numbers'] },
    result = schema.paths
        .filter(s => s.startsWith('email')) // or not or so, if so, maybe
        .reduce((r, p) => {
            var keys = p.split('.'),
                last = keys.pop();

            keys.reduce((o, k) => (o[k] = o[k] || { schema: {} }).schema, r)[last] = p;
            return r;
        }, {});

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

Upvotes: 1

Related Questions