0

I have the following JSON data:

{
    "languageKeys": [{
        "id": 1,
        "project": null,
        "key": "GENERIC.WELCOME",
        "languageStrings": [{
            "id": 1,
            "content": "Welcome",
            "language": {
                "id": 1,
                "key": "EN"
            }
        }]
    }, {
        "id": 2,
        "project": null,
        "key": "GENERIC.HELLO",
        "languageStrings": [{
            "id": 2,
            "content": "Hej",
            "language": {
                "id": 2,
                "key": "DK"
            }
        }, {
            "id": 5,
            "content": "Hello",
            "language": {
                "id": 1,
                "key": "EN"
            }
        }]
    }, {
        "id": 3,
        "project": null,
        "key": "GENERIC.GOODBYE",
        "languageStrings": []
    }]
}

I want that converted into a table where the columns are variable.

The table output should look like the following:

------------------------------------------------
| Key             | EN      | DK  | SE | [...] |
| GENERIC.WELCOME | Welcome |     |    |       |
| GENERIC.HELLO   | Hello   | Hej |    |       |
| GENERIC.GOODBYE |         |     |    |       |
------------------------------------------------

As you can see, the table is dynamic in both rows and columns, and I am struggling to figure out how to map the correct data in each of the "EN", "DK", "SE" [...] fields to the correct column since they are not neccessarily in order when I get them in the JSON response from the API.

I got the following render function so far:

private static renderLanguageKeysTable(languageKeys: ILanguageKey[], languages: ILanguage[]) {
    return <table>
               <thead>
               <tr>
                   <td>Key</td>
                   {languages.map(language =>
                    <td key={language.id}>{language.key}</td>
                )}
               </tr>
               </thead>
               <tbody>
               {languageKeys.map(languageKey =>
                <tr key={languageKey.id}>
                    <td>{languageKey.key}</td>
                    {languages.map(language =>
                        <td key={language.id}>

                        </td>
                    )}
                </tr>
            )}
               </tbody>
           </table>
        ;
}

This works as it should, the only part missing is the data in the columns.

I have tried various variations of filter and map but nonw of them worked out the way I wanted them to.

I am using ReactJS and writing in typescript (es2015)

To clarify a bit: The columns will always be defined by the API, and the rows cannot have an ID pointing to a column that is not there since they are related in the backend. It may however happen that some rows does not have all the columns, in such case they should just be blank

4
  • What should occur for id:3? Commented Jan 20, 2018 at 17:25
  • That looks like TypeScript to me. Maybe you should correct your question to reflect that, and add the tag. Commented Jan 20, 2018 at 17:27
  • @guest271314 - Just an empty row (as shown in the example table) Commented Jan 20, 2018 at 18:04
  • @MattMorgan - Sorry about that, corrected JavaScript -> TypeScript tags now. It is indeed TypeScript. Commented Jan 20, 2018 at 18:04

3 Answers 3

1

I ended up using a different approach from what was suggested (after a good nights sleep and some thinking)

Basically, I created a new component for each individual cell, resulting in the following render on the table side of the code:

private static renderLanguageKeysTable(languageKeys: ILanguageKey[], languages: ILanguage[]) {
    return <table>
               <thead>
               <tr>
                   <th>Key</th>
                   {languages.map(language =>
                    <th key={language.id}>{language.key}</th>
                )}
               </tr>
               </thead>
               <tbody>
               {languageKeys.map(languageKey =>
                <tr key={languageKey.id}>
                    <td>{languageKey.key}</td>
                    {languages.map(language =>
                        <Cell language={language} languageKey={languageKey} key={language.id} />
                    )}
                </tr>
            )}
               </tbody>
           </table>
        ;
}

And the following code for rendering each cell:

import * as React from "react";

export class Cell extends React.Component {
    render() {
        let string: any;
        if (this.props.languageKey && this.props.languageKey.languageStrings) {
            let languageString =
                this.props.languageKey.languageStrings.find((i: any) => i.language.id === this.props.language.id);
            if (languageString === null || languageString === undefined) {
                string = "";
            } else {
                string = languageString.content;
            }
        } else {
            string = "";
        }
        return <td>
            {string}
        </td>;
    }

    props: any;
}
Sign up to request clarification or add additional context in comments.

Comments

0

const findDistinctLang = (langKeys) => {
  let langString = []
  langKeys.forEach((element) => {
    if(element.languageStrings.length !== 0) {
      langString = [...langString, ...element.languageStrings]
    }
    
  })
 
  const langArr = []

  langString.forEach((element) => {
    if (langArr.indexOf(element.language.key) === -1) {
      langArr.push(element.language.key)
    }
  })
  return langArr
}

class Table extends React.Component {
    state = {
      "languageKeys": [{
        "id": 1,
        "project": null,
        "key": "GENERIC.WELCOME",
        "languageStrings": [{
          "id": 1,
          "content": "Welcome",
          "language": {
            "id": 1,
            "key": "EN"
          }
        }]
      }, {
        "id": 2,
        "project": null,
        "key": "GENERIC.HELLO",
        "languageStrings": [{
          "id": 2,
          "content": "Hej",
          "language": {
            "id": 2,
            "key": "DK"
          }
        }, {
          "id": 5,
          "content": "Hello",
          "language": {
            "id": 1,
            "key": "EN"
          }
        }]
      }, {
        "id": 3,
        "project": null,
        "key": "GENERIC.GOODBYE",
        "languageStrings": [{
          "id": 2,
          "content": "Hej",
          "language": {
            "id": 2,
            "key": "DK"
          }
        },{
          "id": 5,
          "content": "XYZ",
          "language": {
            "id": 7,
            "key": "XYZ"
          }
        }]
      }]
    }
    getContentName = (languageSet, langName) => {
     return _.find(languageSet.languageStrings, function(o) { return o.language.key === langName })
    }
    render() {
      const lanKeyArr = findDistinctLang(this.state.languageKeys)
      return ( <
        table >
        <
        thead >
        <
        tr >
        <
        td > Key < /td> {
          lanKeyArr.map((lang) => {
                return ( < td > {
                    lang
                  } < /td>)
                })
            } <
            /tr> <
            /thead> <
          tbody >
          {
          this.state.languageKeys.map((languageSet) => {
          
          return(
           <tr>
           <td>{languageSet.key}</td>
           {[...lanKeyArr].map((element, index) => {
             const contentObj = this.getContentName(languageSet, element)
             return (
             <td>{contentObj && contentObj.content || ""}</td>
             )
           })
           }
           </tr>
          )
           
          })
          }
            <
            /tbody> < /
            table >

        )
      }
    }
    
    ReactDOM.render(<Table />,document.getElementById("root"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

I have implemented based on the test data you provided, Note: you can make it more clean , just giving you an idea by this example

1 Comment

Oh sorry it is typescript , but you can use same logic for typescript also. i realized it after posting my answer
0

You can parse the object and render the table according to the expected rendering.

Note, have minimal experience using ReactJS and have not tried TypeScript

let languages = {"languageKeys":[{"id":1,"project":null,"key":"GENERIC.WELCOME","languageStrings":[{"id":1,"content":"Welcome","language":{"id":1,"key":"EN"}}]},{"id":2,"project":null,"key":"GENERIC.HELLO","languageStrings":[{"id":2,"content":"Hej","language":{"id":2,"key":"DK"}},{"id":5,"content":"Hello","language":{"id":1,"key":"EN"}}]},{"id":3,"project":null,"key":"GENERIC.GOODBYE","languageStrings":[]}]};

const table = document.querySelector("table");
const thead = table.querySelector("thead").querySelector("tr");
const tbody = table.querySelector("tbody");

Object.values(languages.languageKeys).forEach(({key, languageStrings}) => {
  // check if `languageStrings` array has `.length` greater than `0`
  if (languageStrings.length) {
    languageStrings.forEach(({content, language:{key:lang}}) => {
    console.log(key, content, lang);
    // use block scopes
    { 
      // check if the `lang` is already appended to `<thead>`
      if (![...thead.querySelectorAll("td")].find(({textContent}) => textContent === lang)) {
      let td = document.createElement("td");
      td.textContent = lang;
      thead.appendChild(td);
      }
    }
    {
      // append `key`
      let tr = document.createElement("tr");
      let tdKey = document.createElement("td");
      tdKey.textContent = key;
      tr.appendChild(tdKey);
      // append `content`
      let tdContent = document.createElement("td");
      tdContent.textContent = content;
      tr.appendChild(tdKey);
      tr.appendChild(tdContent);
      tbody.appendChild(tr);      
      // append a `<td>` for placing `<td>` in correct column
      // not an optimal approach, adjust if necessary
      if ([...thead.querySelectorAll("td")].findIndex(el => el.textContent === lang) === tr.children.length) {
        tr.insertBefore(document.createElement("td"), tr.lastElementChild);
      };
    }
   })
  } else { 
      // handle empty `languageStrings` array
      let tr = document.createElement("tr");
      let tdKey = document.createElement("td");
      tdKey.textContent = key;
      tr.appendChild(tdKey);
      tbody.appendChild(tr);
  }
})
<table>
  <thead>
    <tr>
      <td>Key</td>
    </tr>
  </thead>
  <tbody>  
  </tbody>
</table>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.