Utilize GroupBy and tally up items within an array using typescript

Here is a representation of my array, which is not a type of string but its own object called MyObject (similar to setter and getter objects in Java)

["Car","model","year","color","price"]
["Table","model","year","color","price"]
["Car","model","year","color","price"]
["Car","model","year","color","price"]
["Laptop","model","year","color","price"]
["Laptop","model","year","color","price"]

I am looking to group this data in TypeScript and count the occurrences of each item in the array (similar to how it's done in SQL)

name  |count
 Car  | 3
Laptop| 2
Table | 1

In my TypeScript file, I have the following code:

private groupByObjects() {
  //this reads all data in the 'allData[]' array from the service
  this.dataService.retrieveData().subscribe(allData => {

});

}

Can anyone assist me in writing this logic in TypeScript?

Answer №1

After numerous attempts, I was finally able to come up with a solution that works perfectly for me. I am sharing it now in the hopes that it will be helpful to anyone facing a similar issue.

function groupByElement(receivedData: ReceivedData, elements: Array<GroupByElement>) {
    let groupElement: GroupByElement = new GroupByElement;

    if (!elements.find(x => x.element== receivedData.element)) {
      groupElement.element= receivedData.element;
      groupElement.count = 1;
      elements.push(groupElement);
    } else {
      this.updateElementCounter = elements.find(x => x.element== receivedData.element)?.count;
      this.updateElementCounter! += 1;
      this.indexElementCount = elements.findIndex(x => x.element== receivedData.element);

      elements[this.indexElementCount].count = this.updateElementCounter!;
    }
  }

Answer №2

Why not give lodash a try?

You can achieve this functionality with just 2 simple lines of code:

private groupByObjects() {
  // This method retrieves data from the service and processes it
  this.dataService.retrieveData().subscribe(allData => {
    const counts = _.countBy(allData, '[0]');
    const data = _.unionBy(allData, '[0]');
    console.log(counts, data);
  });

Don't forget to install and import lodash:

npm install -s lodash

import * as _ from 'lodash';

Answer №3

I really appreciate the concept of a list reducer that utilizes a function returning another function. This allows for flexibility in specifying which key to use, as demonstrated with the key being set to 0 in this case.

interface PropObject {
  [index: string]: number;
}

const groupByCounter = (key : number) => (result : PropObject ,current : string []) => {

  result[current[key]] = result[current[key]] ? result[current[key]] + 1 : 1;
  return result;
};

const data : string [][] = [["Car","model","year","color","price"],
["Table","model","year","color","price"],
["Car","model","year","color","price"],
["Car","model","year","color","price"],
["Laptop","model","year","color","price"],
["Laptop","model","year","color","price"]];

const group = data.reduce(groupByCounter(0),{});
console.log(group);

Link to typescript playground.

Check out the javascript version

Answer №4

You have the ability to achieve this with a time complexity of O(n) using a straightforward reduction

In your specific scenario,

type ApplyFunction<X, T, R> = (all: X, current: T) => R
type GetFunction<T,R> = (t: T) => R

const groupAndApplyByIndex = <T, V extends string | number, R>(data: Array<T>, get: GetFunction<T,V>, apply: ApplyFunction<Record<V, R>, V, R>) => {
  return data.reduce((all, element) => {
    return {
      ...all,
      [get(element)]: apply(all, get(element))
    }
  }, {} as Record<V, R>)
}

You can utilize the above function in your code by simply

private groupByObjects() {
  //this read all data in array allData[] from service
  this.dataService.retrieveData().subscribe(allData => {

    const getX: GetFunction<string[], string> = (t) => t[0]
    const counts = groupAndApplyByIndex(allData, getX, (all, element) => ((all[element] as number || 0) + 1))

    // Implement the rest of your logic here...
  });

Through the use of the method outlined above, you possess a function that is capable of performing tasks such as counting or executing other transformations specified within the apply function

TypeScript Playground

Answer №5

If you're already utilizing rxjs, my suggestion is to stick with it. While I can't guarantee this is the optimal solution and recommend debugging, I personally find this approach enjoyable.

type Item = [string, string, string, string, string]; // consider refining this type definition
type ItemCount = [string, number];

const source$: Observable<Item[]> = of([
  ["Car","model","year","color","price"],
  ["Table","model","year","color","price"],
  ["Car","model","year","color","price"],
  ["Car","model","year","color","price"],
  ["Laptop","model","year","color","price"],
  ["Laptop","model","year","color","price"]
]); // mock data obtained from this.dataService.retrieveData()

source$.pipe(
  mergeMap(res => from(res)), // emit each item individually
  groupBy((item: Item) => item[0]),
  mergeMap((item$: GroupedObservable<Item>) => item$.pipe( 
    count(), // wait for completion, then emit count
    map<number, ItemCount>(c => [item$.key, c]) // convert count into a tuple of [key, count]
  )),
  toArray(), // Wait until all items have been processed, then emit the final ItemCount[]
  map((itemCounts: ItemCount[]) => new Map<string, number>(itemCounts)) // alternatively, use Object.fromEntries(itemCounts)
).subscribe(mapOfCountsPerKey => {
    // implement your desired logic here
});

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

Navigating a SwipeableDrawer in React with scrolling functionality

I'm currently using a swipeable drawer in React from MUI to display lengthy content. My goal is to keep the title visible even when the drawer is closed, and I was able to achieve this through the following method: MUI SwipeableDrawer I've provi ...

Creating a conditional class property requirement in TypeScriptAnother way to implement conditionally required class

I am currently in the process of creating an Event class with a data property that is typed based on the type of the event. Here is the code snippet I have been working on: export interface EventTypes { localeChange: { locale: string; }; translat ...

When using Router.push() in next.js, the error TypeError: products.map is not a function may arise

Currently, I am implementing redux saga in my project. Here is how the state looks: const productList = useSelector((state: RootState) => state.productList); const { loading, error, products, page, pages } = productList; In the useEffect hook, I dispa ...

Adjusting icons based on the length of the text

When I have a title text and an icon, I want to align the icon to the left if the title fits on a single line. However, if the title spans multiple lines, then I need to align the icon to the top. I recently discovered a solution that involves using Javas ...

Revamp Your Service Naming and Nickname with Swagger Codegen IO

Is it possible to customize the Swagger IO CodeGen naming conventions for generating Angular API Service Proxies? Check out Swagger Editor here The current convention combines API, Controller Name, Controller Method, and HTTP Action. public apiProductGet ...

Typescript - Defining string value interfaces

I have a property that can only be assigned one of four specific strings. Currently, I am using a simple | to define these options. However, I want to reuse these types in other parts of my code. How can I create an interface that includes just these 4 va ...

Require type parameter to be of enum type

I have a specific goal in mind: // first.ts export enum First { One, Two, Three } // second.ts export enum Second { One, Two, Three } // factory.ts // For those unfamiliar, Record represents an object with key value pairs type NotWorkingType ...

What is the best way to arrange an array of objects in a descending order in Angular?

private sumArray : any = []; private sortedArray : any = []; private arr1 =['3','2','1']; private arr2 = ['5','7','9','8']; constructor(){} ngOnInit(){ this.sumArray = ...

Is there an issue with this return statement?

retrieve token state$.select(state => { retrieve user access_token !== ''}); This error message is what I encountered, [tslint] No Semicolon Present (semicolon) ...

"Unexpected Alignment Issue with NZ Zorro's Dynamic Columns Feature in the Ant Design NZ-Table Component

I am facing an issue with a table that receives columns dynamically from the server. The headers and data columns are not aligned properly. How can I ensure that they align correctly? <nz-table *ngIf="queryResults" #headerTable [nzData]="queryResults" ...

Obtaining a value from within an Angular 'then' block

I have a unique issue that I haven't been able to find a solution for on StackOverflow: Within an Angular 6 service, I am trying to call a function from another service using TypeScript. Here is the code snippet: Service1: myArray: Array<IMyInte ...

Why are mustaches not functioning as expected in Vue SFC defined by Vite?

I recently ran into a configuration issue with my vite-config.ts file. export default defineConfig({ ... define: { __PRODUCT__: JSON.stringify("My Product") } In my vue sfc template, I have the following code snippet: <div class="footer"> {{ ...

Executing a Prisma query with a selection

My Prisma models involve User, Car, and Reservation entities: model User { id String @id @default(auto()) @map("_id") @db.ObjectId name String? email String? @unique emailVerified DateTime? image ...

Dynamic URL in Angular service for JSON API request

Utilizing an Angular service, I am retrieving JSON data from a specified URL. Take a look at the code snippet provided below: import { Injectable } from '@angular/core'; import {Http,Response} from "@angular/http"; import { Observable } from "rx ...

Failure of React to connect event handlers

LATEST UPDATE: After removing the output entry from my webpack configuration, the React event listeners are now functioning correctly. Currently, I am diving into the world of hand-rolling webpack configurations for a React/TypeScript application for the ...

A mistake occurred during the afterAll function, resulting in a TypeError: Unable to access properties of an undefined entity (specifically, trying to read '

While creating my spec file and settings, I encountered an error in the console: 'An error was thrown in afterAll TypeError: Cannot read properties of undefined (reading 'toLowerCase')', What could be causing this error to appear? H ...

How can I indicate separate paths for the identical dependencies listed in package.json?

Currently, I am in the process of working on an npm package that consists of an example directory designed to run and test the actual package. Within this example directory, I have re-integrated the parent package using "file:..". While this set ...

Angular Route seems unreachable

These are my guidelines for routes: export const appRoutes: Routes = [ { path: "", component: HomeComponent }, { path: '/signup', component: AppComponent }, { path: "**", redirectTo: "/" } ]; Upon attempting to access the URL: http ...

Using NGXS in combination with Firestore or Firebase Authentication

As a newcomer to the topic, I recently discovered that the NGXS state manager for Angular has been released. I am interested in understanding how to connect the Firestore or Firebase Auth to NGXS, but because it is relatively new, I have struggled to fin ...

How can I utilize a variable as the value for ngClass in Angular 2?

Is there a way to use a variable in the ngClass value that gets added to the class list? I have a scenario where I have a set of image sprites, including a base sprite and an active state sprite with "-active" appended to the filename. I insert these sprit ...