You are unable to use T[keyof T] for indexing an empty object

I have been working on implementing a highly efficient group by algorithm based on the insights from this Stack Overflow discussion and attempting to define the types. However, I encountered the following error:

T[keyof T] cannot be used to index {}

Below is my approach:

 static groupBy<T>(xs: T[], key: keyof T) {
        return xs.reduce((rv, x)=> {
            (rv[x[key]] = rv[x[key]] || []).push(x);
            return rv;
        }, {});
    };

Answer №1

If you wish to avoid using any, it is important to accurately define types to specify that the reducer can merge data together effectively.

function groupBy<T extends {[LK in K]: keyof any}, K extends keyof T>(items: T[], key: K) {
  return items.reduce<{[LK2 in T[K]]?: Array<T>};>((result, item) => {
            (result[item[key]] = result[item[key]] || [])?.push(item);
            return result;
        }, {});
};

const output = groupBy([{ test: 'key' }], 'test');

output.key?.length; // 1

T represents an object where the type of the specified key can be used as a reference (for result).

When dealing with reducers - begin with an empty object - it is necessary to clarify that the outcome will be an object wherein the key's value is an array containing entities from the initial set {[LK2 in T[K]]?: Array<T>}

Answer №2

rv: will be any

class A {
  static groupBy<T>(xs: T[], key: keyof T) {
    return xs.reduce((rv: any, x) => {
      (rv[x[key]] = rv[x[key]] || []).push(x);
      return rv;
    }, {});
  }
}

To utilize this function:

interface Person {
  name: string;
  age: number;
  salary: number;
}
const data: Person[] = [
  {name:"deepak", age: 30, salary: 2000},
  {name:"deepak1", age: 32, salary: 1000},
  {name:"deepak", age: 29, salary: 3000}
]
class A {
  static groupBy<T>(xs: T[], key: keyof T) {
    return xs.reduce((rv: any, x) => {
      (rv[x[key]] = rv[x[key]] || []).push(x);
      return rv;
    }, {});
  }
}
console.log(A.groupBy(data, "name"))

Explanation from Lodash library:

groupBy(
            predicate?: Lodash.ListIterator<T, boolean> | Lodash.DictionaryIterator<T, boolean> | string,
            thisArg?: any,
        ): Lodash.Dictionary<T[]>;
        groupBy<R extends {}>(predicate?: R): Lodash.Dictionary<T[]>;

The grouped result is stored in an object where keys are strings or numbers, following a more generic solution.

interface Any<T> {
  [key: string]: T[];
}
interface SMap<T> {
  [key: string]: T;
}
class A {
  static groupBy<T extends SMap<string>>(xs: T[], key: keyof T) {
    return xs.reduce((rv: Any<T>, x) => {
      if (!rv[x[key]]) {
        rv[x[key]] = [];
      }
      rv[x[key]].push(x);
      return rv;
    }, {});
  }
}

Answer №3

This revised version is longer than the original one. It avoids using "any" or "as" casting, making it suitable for various data types as group keys, including unknown ones.

export function groupBy<T>(xs: T[], key: keyof T): Map<unknown, T[]> {
  return xs.reduce((rv: Map<unknown, T[]>, entity: T) => {
    const value = entity[key];
    if (rv.has(value)) {
      rv.get(value)?.push(entity)
    } else {
      rv.set(value, [entity]);
    }
    return rv;
  }, new Map());
};

Here's an example of how you can use this function:

const badgesByTypes = groupBy(currentState.badges, 'type');
for (const [key, values] of badgesByTypes.entries()) {
}

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

Ensure that the button becomes clickable only after text has been inputted into the field

How can I make my button active after entering text into the text box? I would appreciate any help or guidance. Here is the code I have been working on: <body> <script> function ActivateButton() { if (input1.value != null) { send ...

Stop the Decimal in MUI TextField

Having trouble creating a customized Currency Input? You may be experiencing an issue where the textfield prevents the input of decimals. Take a look at the code below for possible solutions. main.ts const [_val, setVal] = React.useState(""); const h ...

Angular form not sending data when using $http.post

I have a form that is submitting to /api/tradelink, but it is not including any body or data. HTML : <form ng-submit="sendTradelink()"> <md-input-container class="md-accent"> <label>Enter your tradelink</ ...

How can I extract a specific value from an array in a JSON object by sorting it?

As someone who is relatively new to JSON and APIs, I am currently working on a project involving an API that returns values in an array structure. Here is an example of the data: { id: 0, text: "one" } { id: 1, text: "two" } ...

Python Selenium: Cannot Click on Element - Button Tag Not Located

TL,DR: My Selenium Python script seems to be having trouble "clicking" on the necessary buttons. Context: Hello. I am working on automating the process of logging into a website, navigating through dropdown menus, and downloading a spreadsheet. Despite ...

Develop JavaScript code to handle multiple JSON objects

I have values stored as objects in an array and I would like to create a JSON structure similar to the one shown below. I have provided both HTML and JS code, but I am struggling with writing the proper javascript to generate this JSON structure. Can someo ...

retrieving information from a data attribute

When setting a data attribute for a user on a link, the code looks like this: <input type="button" class="btn" data-user={"user": "<%= @user.name %>"} value="Start" id="game"> Upon listening for the click event in the JavaScript function, co ...

develop the following application and execute the npm run dev command, but encounter the error message: "Command failed with exit code 1."

After executing npx create-next-app@latest followed by npm run dev, I encountered the error message Command failed with exit code 1.. Additionally, when trying to access https://localhost:3000, an error stating ERR_CONNECTION_REFUSED was displayed. Further ...

execute bower install for the specified bower.json file

Let's say my current working directory is c:\foo\ while the script is running. I want to execute bower from there for the c:\foo\bar\bower.json file. This can be done in npm by using npm install --prefix c:\foo\bar. ...

What is the syntax for accessing a nested object within the .find method?

Currently building an application in node.js. I am struggling with referencing the "email" element in the "userData" object within the Order model when using the find method. Any suggestions on how to properly refer to it? Order model: const orderSchema = ...

Modifying the CSS class of an element does not produce the desired effect after altering its styles using JavaScript

html: <input id="myinput" class="cinput" type="image" src="http://www.foodwater.org.au/images/triple-spiral-3-small-button.jpg"/> <br><br><br><br> <button id="s">set to visible class</button> <button id="h"> ...

Is there a restriction on the number of routes available from Google Maps Directions Service?

Utilizing the Google Maps Directions API, I am able to calculate the distance between the current user's location and various stores. Everything functions properly until I exceed 10 store locations, at which point the API ceases to operate due to its ...

IE compatibility mode causing ckeditor dropdown to be hidden

When using the CKEditor editor bar inside a green div with another div below it, I noticed that when clicking on "font" or any other option that opens a dropdown menu, it gets hidden behind the bottom div. This issue seems to occur in browsers like Chrome ...

jQuery's find method returns a null value

During my Ajax POST request, I encountered an issue where I wanted to replace the current div with the one received from a successful Ajax call: var dom; var target; $.ajax({ type: "POST", url: "http://127.0.0.1/participants", data: "actio ...

Utilizing AJAX to submit a combination of text fields and files in an HTML form

Just starting out with AJAX and JQuery, I'm curious if it's possible to send data from an HTML form, including a text file and two separate text boxes, via an AJAX request. So far, I've managed to send the data from the text boxes but not th ...

How can one change the color of movable tiles to red while keeping the rest of the tiles in their original state in a puzzle game?

In this section of our code, the function moveTile is responsible for moving a tile. How can I modify it so that the hover color changes to red? function moveTile(identity) { var emptyId = findEmptySpace(); if (isEmptyNeighbor(identity)) { document.ge ...

What is the best way to confirm the invocation of super in sinonjs?

My JavaScript/TypeScript class (AAA) extends another class (BBB). The API of class BBB is stable, but the implementation is not yet finalized. I just want to unit test some functions in class AAA. However, I'm facing an issue in creating an instance o ...

Running the Luis Recogniser on ChoicePrompt is a straightforward process that can be easily

In my scenario, I am utilizing a ChoicePrompt which presents the user with two options to choose from. [ { value: 'credit', synonyms: ['titanium', 'world', 'credit', 'mastercard'], } ...

Create seamless communication between Angular application and React build

I am currently engaged in a project that involves integrating a React widget into an Angular application. The component I'm working on functions as a chatbot. Here is the App.tsx file (written in TypeScript) which serves as the entry point for the Rea ...

Setting up Vue CLI 4 for optimal development: Integrating ESLint, Prettier, Airbnb rules, TypeScript, and Vetur

When starting a new project using Vue CLI v4.0.5 and selecting options for TypeScript and Linter / Formatter, you will be presented with pre-configured choices for linting and formatting: ? Choose a linter / formatter configuration: (Use arrow keys) > ...