How can we create dynamic keys for object properties in typescript?

Is there a way in TypeScript to convert an array of objects into an object with keys and arrays dynamically?

For instance, given the following data:

data1 = [
  {a: 'st1', b: 1, c: 1, d: 1, e: 'e1' },
  {a: 'st2', b: 2, c: 2, d: 2, e: 'e2'},
  {a: 'st3', b: 3, c: 3, d: 3, e: 'e3'},
  {a: 'st4', b: 4, c: 4, d: 4, e: 'e4' },
]    

We want to convert it to:

data2= {
   a: ['st1', 'st2', 'st3', 'st4'],
   b: [1, 2, 3, 4],
   c: [1, 2, 3, 4],
   d: [1, 2, 3, 4],
   e: ['e1', 'e2', 'e3', 'e4']
}

While a simple solution using type definitions is:

type Data2Props = {
  a: string[],
  b: number[],
  c: number[],
  d: number[],
  e: string[]
}

const data2: Data2Props = {
  a: [],
  b: [],
  c: [],
  d: [],
  e: []
}

data1?.forEach((item) => {
   data2.a.push(item.a)
   data2.b.push(item.b)
   data2.c.push(item.c)
   data2.d.push(item.d)
   data2.e.push(item.e)
})

But what if the number of keys increases? Is there a more concise solution?

While the following JavaScript code works, it throws errors in TypeScript:

let keys: string[] = ['a', 'b', 'c', 'd', 'e'];

let data2 = {}
keys.forEach((key) => data2[key] = [])
data1?.forEach((item) => {
 keys.forEach((key) => data2[key].push(item[key])
}

In TypeScript, this would result in a typing error.

Answer №1

In the method provided below, numerous type assertions are needed within the transpose function. However, the function's usage is completely type-safe:

const ITEM = {
    a: '',
    b: 0,
    c: 0,
    d: 0,
    e: '',
}

type Item = typeof ITEM

type TransposedItems = {
    [Key in keyof Item]: Item[Key][]
}

function transpose(items: Item[]): TransposedItems {
    const transposed: Partial<TransposedItems> = {}
    for (const key in ITEM) {
        transposed[key as keyof Item] = items.map(item => item[key as keyof Item]) as any[]
    }
    return transposed as TransposedItems
}

const data1: Item[] = [
  {a: 'st1', b: 1, c: 1, d: 1, e: 'e1' },
  {a: 'st2', b: 2, c: 2, d: 2, e: 'e2'},
  {a: 'st3', b: 3, c: 3, d: 3, e: 'e3'},
  {a: 'st4', b: 4, c: 4, d: 4, e: 'e4' },
]
console.log(transpose(data1))

The strategy is to establish a constant ITEM with the same type as the actual elements and let type inference take over. Since ITEM is present at runtime, we can iterate through its keys, a task that is challenging to accomplish if it only existed as a type during compilation.

Alternatively, you could utilize the first item in the array for this purpose, but issues may arise if the objects do not have identical structures. Moreover, this approach only works if the array is not empty.

Answer №2

In typescript, you have the option to implement something similar to the example below

let info1:{[key:string]:any}[] = [
  {a: 'st1', b: 1, c: 1, d: 1, e: 'e1' },
  {a: 'st2', b: 2, c: 2, d: 2, e: 'e2'},
  {a: 'st3', b: 3, c: 3, d: 3, e: 'e3'},
  {a: 'st4', b: 4, c: 4, d: 4, e: 'e4' },
];

let keys: string[] = ['a', 'b', 'c', 'd', 'e'];

let info2:{[key:string]:any[]} = {}
keys.forEach((key) => info2[key] = [])
info1?.forEach((item) => {
 keys.forEach((key) => info2[key].push(item[key]))
});

console.log(info2);

Playground link

Alternatively, you can achieve the desired outcome without explicitly defining the keys array

let info1:{[key:string]:string|number}[] = [
      {a: 'st1', b: 1, c: 1, d: 1, e: 'e1' },
      {a: 'st2', b: 2, c: 2, d: 2, e: 'e2'},
      {a: 'st3', b: 3, c: 3, d: 3, e: 'e3'},
      {a: 'st4', b: 4, c: 4, d: 4, e: 'e4' },
    ]  ;

  let info2:{[key:string]:any[]}={};
    info1.forEach(obj=>{
    Object.keys(obj).forEach(key=>{
    let val=obj[key];
     if(info2[key]){
     info2[key].push(val);
     }else{
     info2[key]=[val];
     }
    })
  });

  console.log(info2)

Playground link

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The problem of parameter being NULL in a post request in a .Net Core 3.0 Angular application

This is my first venture into the world of .Net Core Angular projects, so I apologize if my question appears to be too basic. Despite researching similar issues, I am still unable to resolve my problem, which leads me to believe that I must be making a mis ...

Some images fail to load on Ember in the production environment

I am facing an issue with my Ember-cli 1.13 application where all images are loading correctly except those in a specific component. The component is named "list-item" and is defined as follows: {{list-item url="list-url" name="List Name" price="240"}} I ...

"Obtaining mouse coordinates in VueJS: A step-by-step guide

I am using a component that is activated by v-on:click="someMethod". Is there a way to retrieve the X and Y coordinates of the mouse click within this component? Extra context: The component I'm working with is an HTML5 Canvas element. ...

Ensure that compiler errors are eliminated for object properties that do not exist

Coming from a JavaScript background, I recently began working with Angular 2 and TypeScript. Below is a snippet of my code: export class AddunitsComponent implements OnInit { public centers:any; constructor(){ this.centers = {}; }} In my view, I h ...

Differences in file loading in Node.js: comparing the use of .load versus command-line

Currently, I am in the process of developing a basic server using vanilla JavaScript and Node.js. For this purpose, I have created a file named database.js, which includes abstractions for database interactions (specifically with redis). One of my objecti ...

Utilizing the power of Datatables through AJAX calls with HTML Forms

Hello, I am currently working on incorporating djangorestframework-datatables with the datatables' JQuery plugin. My task involves loading a large table (consisting of approximately 15000 entries, paginated) with the serverSide option enabled. Enabli ...

The functionality of the Ionic menu button becomes disabled once the user has successfully logged in

Having trouble clicking the button after taking a test. Situation: Once logged in -> user takes a test and submits -> redirected to home page. However, unable to click on "Menu button" on the home page. In my Login.ts file: if (this.checker == " ...

What is the standard text displayed in a textarea using jQuery by default

My goal is to display default text in a textarea upon page load. When the user clicks on the textarea, I want the default text to disappear. If the user clicks into the textarea without typing anything and then clicks out of it, I'd like the default t ...

JavaScript - utilizing a confirmation dialog box within a function

My JavaScript code was working well because I received some fantastic solutions here yesterday. I am now wondering if I can enhance this JavaScript with another query. Currently, the query triggers an alert when the number is greater than 199, and it is fu ...

Use JavaScript and Handlebars to compile any templates that have not yet been compiled

I have an HTML file containing various templates that I want to compile when the page loads. I'm looking for a way to either compile and store all templates in an array upfront, or compile templates on the fly as users navigate through the SPA. I am u ...

Tips for retrieving data for client components on the server side

When referring to Client Components, I am talking about files that use use client at the top, while Server Components are files that utilize use server accordingly. I haven't come across any mention of fetching data on the server side and directly pa ...

What is the best way to select a specific value from JSON (Webhook) Data?

I am looking for a way to extract and store a specific value from a JSON data into a variable. Specifically, I want to save the value of Name (John) in a variable like this: var name = "". I attempted using var name = data.Name but it is not wor ...

What steps are needed to create a transport directly from the Console?

In my current setup, this is the code I have: const logger = new winston.Logger(); logger.add(winston.transports.Console, { level: environment === 'development' ? 'silly' : 'info', colorize: true, prettyPrint: true }); ...

Enclose the serialized JSON string in single quotes to avoid parsing errors caused by using double quotes

My current task involves utilizing the JSON.stringfy() and JSON.parse() methods to serialize and deserialize JSON data. Everything is functioning properly when working with a single quoted serialized string. However, I am encountering an issue when attempt ...

Incorporate a hyperlink into a React Material-UI DataGrid

While utilizing the DataGrid component from Material-UI, I am trying to add a link to the end of each row. However, the output is currently displaying as: ( [object Object] ). https://i.stack.imgur.com/2k3q2.png I would like for it to show the record ID, ...

The Model Viewer module is unable to load the three-dimensional model

Attempting to incorporate a 3D model into my website using the modelviewer library, but encountering difficulties in loading the file as shown below: Just to note, I am utilizing github pages with a custom domain from godaddy which may be contributing to ...

Unexpected behavior when using Async.map in conjunction with async.waterfall

Utilizing Async, I am using async.map() to connect my data array with a function and incorporating async.waterfall() within that function to execute functions in a series. However, the waterfall function is not functioning as anticipated. I have also attem ...

Steps for modifying the look of a button to display an arrow upon being clicked with CSS

Looking to enhance the visual appearance of a button by having an arrow emerge from it upon clicking, all done through CSS. Currently developing a React application utilizing TypeScript. Upon clicking the next button, the arrow should transition from the ...

How can I pass DOCUMENT in Angular?

In my directive, I use dependency injection to access the DOCUMENT and set up an event listener: constructor(@Inject(DOCUMENT) private document: Document) {} ngOnInit() { this.document.addEventListener('click', this.clicked, true); } @Bound ...

Can you identify the primary parameter used in the Parse.User.signUp() method?

Utilizing Parse.com as the database for my app, I am working on streamlining the code in my signup view. user.set("username",$scope.user.email); user.set("email",$scope.user.email); user.set("password",$scope.user.password); user.signUp(null, ...