Francisco EP
Francisco EP

Reputation: 1

Creating a new object using reduce in JS

I've been dealing with creating an object with reduce from a nested array. The object needs to have the _iud keys with the value of initialValue, from the object that contains both keys. I made a function that can iterate through them but can't return the new object with all the new properties I got. I've tried to use the shallow and deep copy to clone acc but can't make it :( Could anyone give me a hand?

This is the nested array

export const formData = [
  {
component: 'page',
label: 'Page 1',
_uid: '0c946643-5a83-4545-baea-055b27b51e8a',
fields: [
  {
    component: 'field_group',
    label: 'Name',
    _uid: 'eb169f76-4cd9-4513-b673-87c5c7d27e02',
    fields: [
      {
        component: 'text',
        label: 'First Name',
        initialValue: '2345432',
        type: 'text',
        _uid: '5b9b79d2-32f2-42a1-b89f-203dfc0b6b98',
      },
      {
        component: 'text',
        label: 'Last Name',
        initialValue: '2345432',
        type: 'text',
        _uid: '6eff3638-80a7-4427-b07b-4c1be1c6b186',
      },
    ],
  },
  {
    component: 'text',
    label: 'Email',
    initialValue: '2345432',
    type: 'email',
    _uid: '7f885969-f8ba-40b9-bf5d-0d57bc9c6a8d',
  },
  {
    component: 'text',
    label: 'Phone',
    initialValue: '2345432',
    type: 'text',
    _uid: 'f61233e8-565e-43d0-9c14-7d7f220c6020',
  },
],
  },
  {
component: 'page',
label: 'Page 2',
_uid: '3a30803f-135f-442c-ab6e-d44d7d7a5164',
fields: [
  {
    component: 'options',
    label: 'Radio Buttons',
    type: 'radio',
    initialValue: '2345432',
    _uid: 'bd90f44a-d479-49ae-ad66-c2c475dca66b',
    options: [
      {
        component: 'option',
        label: 'Option 1',
        value: 'one',
      },
      {
        component: 'option',
        label: 'Option 2',
        value: 'two',
      },
    ],
  },
  {
    component: 'text',
    label: 'Conditional Field',
    type: 'text',
    _uid: 'bd90f44a-d479-49ae-ad66-c2c475daa66b',
    initialValue: '2345432',
    conditional: {
      value: 'two',
      field: '3a30803f-135f-442c-ab6e-d44d7d7a5164_bd90f44a-d479-49ae-ad66-c2c475dca66b',
    },
  },
],
  },
  {
component: 'page',
label: 'Page 3a',
_uid: 'cd392929-c62e-4cdb-b4dd-914035c1cc8d',
initialValue: '2345432',
conditional: {
  value: 'one',
  field: '3a30803f-135f-442c-ab6e-d44d7d7a5164_bd90f44a-d479-49ae-ad66-c2c475dca66b',
},
fields: [
  {
    component: 'options',
    label: 'More radio buttons',
    type: 'radio',
    _uid: 'a15bef56-ab67-4b98-a781-4441cc3bba56',
    initialValue: '2345432',
    options: [
      { component: 'option', label: 'Option 1', value: 'one' },
      { component: 'option', label: 'Option 2', value: 'two' },
    ],
  },
],
  },
  {
component: 'page',
label: 'Page 3b',
_uid: '1dd4ec7c-fb53-47f4-af1b-1ab8f805b888',
conditional: {
  value: 'two',
  initialValue: '2345432',
  field: '3a30803f-135f-442c-ab6e-d44d7d7a5164_bd90f44a-d479-49ae-ad66-c2c475dca66b',
},
fields: [
  {
    component: 'options',
    label: 'Something to toggle',
    type: 'radio',
    _uid: '3ca9237d-e225-4950-a298-f81ce996cb85',
    options: [
      {
        component: 'option',
        label: 'Option 1',
        value: 'one',
      },
      { component: 'option', label: 'Option 2', value: 'two' },
    ],
  },
  {
    component: 'field_group',
    label: 'Name',
    _uid: 'b8406cb5-ff0d-4a83-a8f8-99740b6d91f7',
    fields: [
      {
        component: 'text',
        label: 'First Name',
        initialValue: '2345432',
        type: 'text',
        _uid: 'c6e065e1-dbcb-44ea-831f-ac3af889e964',
      },
      {
        component: 'text',
        label: 'Last Name',
        initialValue: '2345432',
        type: 'text',
        _uid: 'e279ba9c-3c9b-4df8-b267-d14b3c2adcdd',
      },
    ],
  },
  {
    component: 'text',
    label: 'Email',
    initialValue: '2345432',
    type: 'email',
    _uid: 'a95208a0-7673-48a8-b704-2fb408fa6eec',
  },
  {
    component: 'text',
    label: 'Phone',
    initialValue: '2345432',
    type: 'text',
    _uid: '8dde5083-0619-42d6-8fc7-0563c35d03ad',
  },
],
  },
  {
component: 'page',
label: 'Page 4',
_uid: '0c946643-5a83-4545-baea-065b27b51e8a',
fields: [
  {
    component: 'text',
    label: 'Final Comment',
    initialValue: '2345432',
    type: 'text',
    _uid: 'f61231e8-565e-43d0-9c14-7d7f220c6020',
  },
],
  },
]

The function:

 function getInitialValues(formData = []) {
    return Array.from(formData).reduce((acc, currentValue, idx) => {
      const arrayOfStrings = ['page', 'field_group', 'options']
      const str = currentValue.component
      const found = arrayOfStrings.find((v) => str === v)
      if (found) {
        // console.log('entro', currentValue)
        getInitialValues(currentValue?.fields)

        return acc
      }

      if (
        (currentValue.component === 'text' || currentValue.component === 'select') &&
        currentValue.label === 'Conditional Field'
      ) {
        acc[currentValue._uid] = currentValue?.initialValue

        return acc
      }
      return acc
    }, {})
  }

My expected result is something like this from all the _uids that has initialValues (only for references)

{
5b9b79d2-32f2-42a1-b89f-203dfc0b6b98: '2345432',
5b9b79d2-32f2-42a1-b89f-203dfc0b6b97: '2345431',
5b9b79d2-32f2-42a1-b89f-203dfc0b6b96: '2345430',

}

Upvotes: 0

Views: 163

Answers (3)

Scott Sauyet
Scott Sauyet

Reputation: 50787

I find it easier to separate the extraction of your _uid/initialValue properties from the formatting of these into an output object.

Here is a recursive extract function and a simple convert function that builds on it.

const extract = (xs) =>
  xs .flatMap (({initialValue, _uid, fields = []}) =>
    [[_uid, initialValue], ...extract (fields)]
  ) .filter (([_, init]) => init != null)

const convert = xs =>
  Object .fromEntries (extract (xs))

const formData = [{component: "page", label: "Page 1", _uid: "0c946643-5a83-4545-baea-055b27b51e8a", fields: [{component: "field_group", label: "Name", _uid: "eb169f76-4cd9-4513-b673-87c5c7d27e02", fields: [{component: "text", label: "First Name", initialValue: "2345432", type: "text", _uid: "5b9b79d2-32f2-42a1-b89f-203dfc0b6b98"}, {component: "text", label: "Last Name", initialValue: "2345432", type: "text", _uid: "6eff3638-80a7-4427-b07b-4c1be1c6b186"}]}, {component: "text", label: "Email", initialValue: "2345432", type: "email", _uid: "7f885969-f8ba-40b9-bf5d-0d57bc9c6a8d"}, {component: "text", label: "Phone", initialValue: "2345432", type: "text", _uid: "f61233e8-565e-43d0-9c14-7d7f220c6020"}]}, {component: "page", label: "Page 2", _uid: "3a30803f-135f-442c-ab6e-d44d7d7a5164", fields: [{component: "options", label: "Radio Buttons", type: "radio", initialValue: "2345432", _uid: "bd90f44a-d479-49ae-ad66-c2c475dca66b", options: [{component: "option", label: "Option 1", value: "one"}, {component: "option", label: "Option 2", value: "two"}]}, {component: "text", label: "Conditional Field", type: "text", _uid: "bd90f44a-d479-49ae-ad66-c2c475daa66b", initialValue: "2345432", conditional: {value: "two", field: "3a30803f-135f-442c-ab6e-d44d7d7a5164_bd90f44a-d479-49ae-ad66-c2c475dca66b"}}]}, {component: "page", label: "Page 3a", _uid: "cd392929-c62e-4cdb-b4dd-914035c1cc8d", initialValue: "2345432", conditional: {value: "one", field: "3a30803f-135f-442c-ab6e-d44d7d7a5164_bd90f44a-d479-49ae-ad66-c2c475dca66b"}, fields: [{component: "options", label: "More radio buttons", type: "radio", _uid: "a15bef56-ab67-4b98-a781-4441cc3bba56", initialValue: "2345432", options: [{component: "option", label: "Option 1", value: "one"}, {component: "option", label: "Option 2", value: "two"}]}]}, {component: "page", label: "Page 3b", _uid: "1dd4ec7c-fb53-47f4-af1b-1ab8f805b888", conditional: {value: "two", initialValue: "2345432", field: "3a30803f-135f-442c-ab6e-d44d7d7a5164_bd90f44a-d479-49ae-ad66-c2c475dca66b"}, fields: [{component: "options", label: "Something to toggle", type: "radio", _uid: "3ca9237d-e225-4950-a298-f81ce996cb85", options: [{component: "option", label: "Option 1", value: "one"}, {component: "option", label: "Option 2", value: "two"}]}, {component: "field_group", label: "Name", _uid: "b8406cb5-ff0d-4a83-a8f8-99740b6d91f7", fields: [{component: "text", label: "First Name", initialValue: "2345432", type: "text", _uid: "c6e065e1-dbcb-44ea-831f-ac3af889e964"}, {component: "text", label: "Last Name", initialValue: "2345432", type: "text", _uid: "e279ba9c-3c9b-4df8-b267-d14b3c2adcdd"}]}, {component: "text", label: "Email", initialValue: "2345432", type: "email", _uid: "a95208a0-7673-48a8-b704-2fb408fa6eec"}, {component: "text", label: "Phone", initialValue: "2345432", type: "text", _uid: "8dde5083-0619-42d6-8fc7-0563c35d03ad"}]}, {component: "page", label: "Page 4", _uid: "0c946643-5a83-4545-baea-065b27b51e8a", fields: [{component: "text", label: "Final Comment", initialValue: "2345432", type: "text", _uid: "f61231e8-565e-43d0-9c14-7d7f220c6020"}]}]

console .log (convert (formData))

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

The question also seems to imply that you want to do an update of the data. I would think of that as a separate step, which you already seem to be able to do.

Upvotes: 1

Kinglish
Kinglish

Reputation: 23654

I found a simple forEach loop with recursion to be easier.

const formData = [{
    component: 'page',
    label: 'Page 1',
    _uid: '0c946643-5a83-4545-baea-055b27b51e8a',
    fields: [{
        component: 'field_group',
        label: 'Name',
        _uid: 'eb169f76-4cd9-4513-b673-87c5c7d27e02',
        fields: [{
            component: 'text',
            label: 'First Name',
            initialValue: '2345432',
            type: 'text',
            _uid: '5b9b79d2-32f2-42a1-b89f-203dfc0b6b98',
          },
          {
            component: 'text',
            label: 'Last Name',
            initialValue: '2345432',
            type: 'text',
            _uid: '6eff3638-80a7-4427-b07b-4c1be1c6b186',
          },
        ],
      },
      {
        component: 'text',
        label: 'Email',
        initialValue: '2345432',
        type: 'email',
        _uid: '7f885969-f8ba-40b9-bf5d-0d57bc9c6a8d',
      },
      {
        component: 'text',
        label: 'Phone',
        initialValue: '2345432',
        type: 'text',
        _uid: 'f61233e8-565e-43d0-9c14-7d7f220c6020',
      },
    ],
  },
  {
    component: 'page',
    label: 'Page 2',
    _uid: '3a30803f-135f-442c-ab6e-d44d7d7a5164',
    fields: [{
        component: 'options',
        label: 'Radio Buttons',
        type: 'radio',
        initialValue: '2345432',
        _uid: 'bd90f44a-d479-49ae-ad66-c2c475dca66b',
        options: [{
            component: 'option',
            label: 'Option 1',
            value: 'one',
          },
          {
            component: 'option',
            label: 'Option 2',
            value: 'two',
          },
        ],
      },
      {
        component: 'text',
        label: 'Conditional Field',
        type: 'text',
        _uid: 'bd90f44a-d479-49ae-ad66-c2c475daa66b',
        initialValue: '2345432',
        conditional: {
          value: 'two',
          field: '3a30803f-135f-442c-ab6e-d44d7d7a5164_bd90f44a-d479-49ae-ad66-c2c475dca66b',
        },
      },
    ],
  },
  {
    component: 'page',
    label: 'Page 3a',
    _uid: 'cd392929-c62e-4cdb-b4dd-914035c1cc8d',
    initialValue: '2345432',
    conditional: {
      value: 'one',
      field: '3a30803f-135f-442c-ab6e-d44d7d7a5164_bd90f44a-d479-49ae-ad66-c2c475dca66b',
    },
    fields: [{
      component: 'options',
      label: 'More radio buttons',
      type: 'radio',
      _uid: 'a15bef56-ab67-4b98-a781-4441cc3bba56',
      initialValue: '2345432',
      options: [{
          component: 'option',
          label: 'Option 1',
          value: 'one'
        },
        {
          component: 'option',
          label: 'Option 2',
          value: 'two'
        },
      ],
    }, ],
  },
  {
    component: 'page',
    label: 'Page 3b',
    _uid: '1dd4ec7c-fb53-47f4-af1b-1ab8f805b888',
    conditional: {
      value: 'two',
      initialValue: '2345432',
      field: '3a30803f-135f-442c-ab6e-d44d7d7a5164_bd90f44a-d479-49ae-ad66-c2c475dca66b',
    },
    fields: [{
        component: 'options',
        label: 'Something to toggle',
        type: 'radio',
        _uid: '3ca9237d-e225-4950-a298-f81ce996cb85',
        options: [{
            component: 'option',
            label: 'Option 1',
            value: 'one',
          },
          {
            component: 'option',
            label: 'Option 2',
            value: 'two'
          },
        ],
      },
      {
        component: 'field_group',
        label: 'Name',
        _uid: 'b8406cb5-ff0d-4a83-a8f8-99740b6d91f7',
        fields: [{
            component: 'text',
            label: 'First Name',
            initialValue: '2345432',
            type: 'text',
            _uid: 'c6e065e1-dbcb-44ea-831f-ac3af889e964',
          },
          {
            component: 'text',
            label: 'Last Name',
            initialValue: '2345432',
            type: 'text',
            _uid: 'e279ba9c-3c9b-4df8-b267-d14b3c2adcdd',
          },
        ],
      },
      {
        component: 'text',
        label: 'Email',
        initialValue: '2345432',
        type: 'email',
        _uid: 'a95208a0-7673-48a8-b704-2fb408fa6eec',
      },
      {
        component: 'text',
        label: 'Phone',
        initialValue: '2345432',
        type: 'text',
        _uid: '8dde5083-0619-42d6-8fc7-0563c35d03ad',
      },
    ],
  },
  {
    component: 'page',
    label: 'Page 4',
    _uid: '0c946643-5a83-4545-baea-065b27b51e8a',
    fields: [{
      component: 'text',
      label: 'Final Comment',
      initialValue: '2345432',
      type: 'text',
      _uid: 'f61231e8-565e-43d0-9c14-7d7f220c6020',
    }, ],
  },
]

function getInitialValues(formData = [], output=[]) {
  formData.forEach(obj => {
    // first check for the Conditional Field label
    let found = obj.hasOwnProperty('label') && obj.label === "Conditional Field";
    // then loop through the component fields if neccesary
    if (!found) ['select','text','options'].forEach(val => {
    if (obj.hasOwnProperty('component') && obj.component === val) found = true;})
    // if found update the output array
    if (found) output.push({[obj._uid]: obj.initialValue})
    // check for recursion
    if (obj.hasOwnProperty('fields')) output = getInitialValues(obj.fields, output)
  })
  return output;
}

console.log(getInitialValues(formData))

Upvotes: 0

Nir Alfasi
Nir Alfasi

Reputation: 53525

The problem is in:

 if (found) {
    // console.log('entro', currentValue)
    getInitialValues(currentValue?.fields)

    return acc
  }

this call to getInitialValues is supposed to return a result that is not saved into acc.

One way to correct it is to do something like this:

    if (found) {
        const res = getInitialValues(currentValue.fields);
        return Object.assign(acc, res);
    }

There may be other issues as well, but this is the one that stood up for me.

Note: two out of three of the uids in the "expected results" do not appear in the first code snippet (the example of formData). As for the other one (with uid: 5b9b79d2-32f2-42a1-b89f-203dfc0b6b98) it has a label of 'First Name' not 'Conditional Field' - that's why it doesn't show up in the output.

(Full) Corrected code

function getInitialValues (formData = []) {
    return formData.reduce((acc, currentValue) => {
        const arrayOfStrings = ['page', 'field_group', 'options'];
        const str = currentValue.component;
        const found = arrayOfStrings.find((v) => str === v);
        if (found) {
            const res = getInitialValues(currentValue.fields);
            return Object.assign(acc, res);
        }

        if (
            (currentValue.component === 'text' || currentValue.component === 'select') &&
            currentValue.label === 'Conditional Field'
        ) {
            if (currentValue.initialValue) {
                acc[currentValue._uid] = currentValue.initialValue;
            }
        }
        return acc;
    }, {});

Upvotes: 0

Related Questions