Home > Enterprise >  JS - How to concat a dynamic variable name using two objects?
JS - How to concat a dynamic variable name using two objects?

Time:12-09

I have this first array:

const columns = [
    {
        header: "Nome",
        field: "attributes.name",
        sort: true
    },
    {
        header: "Empresa",
        field: "attributes.company",
        sort: true
    }
];

And a second array fill with some data.

props.data.data has the following array:

[
    {
        "id": "3",
        "type": "Testimonial",
        "attributes": {
            "name": "Dr. Carla Jéssica Balestero Sobrinho",
            "image_path": null,
            "company": "Quintana Comercial Ltda.",
            "text": "Ao cabo de um dos pés adeante e apagou o escripto. Padua saiu ao quintal, a ver o que se queria matar. Minha mãe quiz saber porque a denuncia do meu coração, se vontade de servir a você, falará com.",
            "status": "unavailable",
            "created_at": "2022-11-15T19:35:13.000000Z",
            "updated_at": "2022-11-15T19:35:13.000000Z"
        }
    },
    {
        "id": "5",
        "type": "Testimonial",
        "attributes": {
            "name": "Maykel Esser2",
            "image_path": "/Users/maykel/Documents/GitHub/clima-api/storage/MjCyVPbHiTteDc6WmkhK47OgV3SbcDuy3yO2X1yR.png",
            "company": "Faria-Molina",
            "text": "O que aqui viveu e morreu... E explicou-me um dia que esta razão a moveu. --Levanta, Capitú! Não quiz, não levantou a cabeça, podia ficar tonta, machucar o pescoço. Cheguei a pensar nella durante as.",
            "status": "available",
            "created_at": "2022-11-15T19:35:13.000000Z",
            "updated_at": "2022-11-19T20:58:40.000000Z"
        }
    }
]

I'm trying to mount a HTML table in a react component using map functions, but due to the dynamic column field, the value returns undefined and i don't know why.

import React from 'react';

export default function Table(props){

    console.log(props.columns[0].field); // attributes.name
    console.log(props.data.data[0].attributes.name) // Dr. Carla
    console.log(props.data.data[0][props.columns[0].field]) // undefined (but should return Dr. Carla as well)

    return (
        <>
            <table>
                <thead>
                    <tr>
                        {props.columns.map((column, index) => (
                            <th key={index}>{column.header}</th>
                        ))}
                    </tr>
                </thead>
                <tbody>
                    {props.data.data.map((row, index) => (
                        <tr key={index}>
                            {props.columns.map((column, index) => (
                                <td key={index}>{row[column.field]}</td> <-- HERE
                            ))}
                        </tr>
                    ))}
                </tbody>
            </table>
        </>
    );
}

So, how can i concat the dynamic value from column array to get the right value?

CodePudding user response:

You can only access one property at a time. There actually could be a property called 'attribute.name' since keys are strings. If you know that your properties won't contain a '.' in their individual names, you could create a function to split them out:

const props = {
  columns: [{ field: 'attributes.name' }],
  data: {
    data: [{
      attributes: {
        name: 'Dr. Carla'
      }
    }]
  }
}

const getPropertyValue = (obj, props) => props.split('.')
.reduce((p, c) => p[c], obj)

console.log(props.columns[0].field); // attributes.name
console.log(props.data.data[0].attributes.name) // Dr. Carla
console.log(getPropertyValue(props.data.data[0], props.columns[0].field))

const getProperty = (obj, props) => props.split('.')
.reduce((p, c) => p[c], obj)

console.log(props.columns[0].field); // attributes.name
console.log(props.data.data[0].attributes.name) // Dr. Carla
console.log(getPropertyValue(props.data.data[0], props.columns[0].field) 

The split gives an array like ['attribute', 'name']. The reduce starts with an object, and for each string in the array returns the value of the property with that given key.

CodePudding user response:

The issue is that your field in the column definition is telling you the path in the data object. The simplest solution would be to just drop the 'attributes.' part from the definition and use row.attributes[column.field] (this of course assumes you always need to access fields from 'attributes'. However, if you need to access any nested property or don't control the columns definition, you can create an accessor method to parse the path and recurse down to get the value.

Something like this should work:

import React from 'react';

const getValue = (parts, current) => {
  const [part, ...rest] = parts
  //If you are on the last part, return the value.
  if (rest.length === 0) return current[part]
  //Otherwise, recurse into the object and pass the remaining parts
  return getValue(rest, current[part])
}

export default function Table(props){

    console.log(props.columns[0].field); // attributes.name
    console.log(props.data.data[0].attributes.name) // Dr. Carla
    console.log(props.data.data[0][props.columns[0].field]) // undefined (but should return Dr. Carla as well)

    return (
        <>
            <table>
                <thead>
                    <tr>
                        {props.columns.map((column, index) => (
                            <th key={index}>{column.header}</th>
                        ))}
                    </tr>
                </thead>
                <tbody>
                    {props.data.data.map((row, index) => (
                        <tr key={index}>
                            {props.columns.map((column, index) => (
                                <td key={index}>{getValue(column.field.split('.'), row)}</td>
                            ))}
                        </tr>
                    ))}
                </tbody>
            </table>
        </>
    );
}

CodePudding user response:

The issue is that your object doesn't have an "attributes.name" property. It has an "attributes" property that is an object with a "name" property. Consider this example:

const foo = {
  "attributes.name": "alice",
  attributes: {
    name: "bob"
  }
}

foo["attributes.name"] is "alice", while foo.attributes.name is "bob". Not the same thing.

You'd have to split the path on the dots and walk the object until you get to the leaf node. I've doodled an example in the snippet below, but it's imperfect and just for illustrative purposes. It doesn't handle missing properties and relies on a bunch of assumptions.

There are off-the-shelf utilities that can do this for you if you don't want to write your own. (And jason's implementation is better than mine.)

const entry = {
    "id": "3",
    "type": "Testimonial",
    "attributes": {
      "name": "Dr. Carla Jéssica Balestero Sobrinho",
      "image_path": null,
      "company": "Quintana Comercial Ltda.",
      "text": "Ao cabo de um dos pés adeante e apagou o escripto. Padua saiu ao quintal, a ver o que se queria matar. Minha mãe quiz saber porque a denuncia do meu coração, se vontade de servir a você, falará com.",
      "status": "unavailable",
      "created_at": "2022-11-15T19:35:13.000000Z",
      "updated_at": "2022-11-15T19:35:13.000000Z"
    }
  }
  
function getAttr(object, path) {
  const segments = path.split('.');
  let value = object;
  while(segments.length) {
    value = value[segments.shift()];
  }
  return value;
}

console.log(getAttr(entry, 'attributes.name'));

  • Related