Utilizing *ngfor in Angular 7 to Group and Display Data

I currently have incoming data structured like this in my Angular application

Visit this link to see the data format

https://i.stack.imgur.com/jgEIw.png

What is the best way to group and sort the data by State and County, and present it in a table as shown below?

https://i.stack.imgur.com/azRyC.png

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  data = [
    { state: 'MN', county: '1', item: 0.297 },
    { state: 'MN', county: '1', item: 0.04 },
      { state: 'CA', county: '2', item: 0.019 },
    { state: 'MN', county: '1', item: 0.0374 }, 
    { state: 'CA', county: '2', item: 0.037 }
]
}
 <table >
        <tr>
          <th>State</th>
          <th>County</th>
          <th>Item</th>
        </tr>
        <ng-container *ngFor="let dataitem of data">
        <tr>
          <td>{{dataitem.state}}</td>
          <td>{{dataitem.county}}</td>
          <td>{{dataitem.item}}</td>
        </tr>
        </ng-container>
    </table>

Answer №1

If your data is already ordered, you can utilize the 'let i = index' method to compare if the previous data item is the same using a conditional operator.

<ng-container *ngFor="let dataitem of data; let i=index">
<tr>
  <td>{{i > 0 && data[i-1].state == dataitem.state ? '' : dataitem.state}}</td>
  <td>{{i > 0 && data[i-1].county == dataitem.county ? '' : dataitem.county}}</td>
  <td>{{dataitem.item}}</td>
</tr>
</ng-container>

Answer №2

Utilize the power of lodash to organize your data into separate arrays.

Learn more about lodash's groupBy method here!

import { groupBy } from 'lodash-es'
const countries = groupBy(data, 'country')

You can display multiple tbody elements in your table

<tbody *ngFor="let country of countries">...</tbody>

Answer №3

By following the instructions in this helpful answer, you can preprocess your data or use a pipe to generate nested objects.

Here is an example inspired by the linked solution:

key = 'state';
data = data.reduce((data, x) => {
    (data[x[key]] = data[x[key]] || []).push(x);
    return data;
  }, {});

The above code snippet should result in something like this:

data = [
    { 'MN': [
        { county: '1', item: 0.297 }
        ....
    ]}
]

Alternatively, if your list is sorted, you can employ clever *ngIf and index logic. Check out more details here. Here's an example of how to do it:

<div *ngFor="let item of list; let i = index;">
  <div *ngIf="i > 0 && item.state !== list[i-1].state">{{item.state}}</div>
</div>

Answer №4

Check out this fully functional solution along with a Stackblitz Demo demonstrating cell merging capabilities.

To achieve the desired effect, you need to calculate a row span for each item and assign it to the rowspan attribute of the respective td. Additionally, conditionally render the td to only display it for the first occurrence of each state.

An effective strategy involves preprocessing an array sorted by state and county, incorporating added span properties for both states and counties.

Determining these span properties involves tallying the child count for each state and county within that state by filtering the original data array.

The objective is to structure the array as follows:

[
  {state: "CA", county: "2", item: 0.019, stateSpan: 3, countySpan: 2},
  {state: "CA", county: "2", item: 0.037, stateSpan: 0, countySpan: 0},
  {state: "CA", county: "3", item: 0.14, stateSpan: 0, countySpan: 1},
  {state: "MN", county: "1", item: 0.297, stateSpan: 4, countySpan: 3},
  {state: "MN", county: "1", item: 0.04, stateSpan: 0, countySpan: 0},
  {state: "MN", county: "1", item: 0.0374, stateSpan: 0, countySpan: 0},
  {state: "MN", county: "3", item: 0.14, stateSpan: 0, countySpan: 1}
]

Referencing the code snippet below:

 <table>
    <tr>
      <th>State</th>
      <th>County</th>
      <th>Item</th>
    </tr>
    <tr *ngFor="let item of dataExt">
      <td [attr.rowspan]="item.stateSpan" *ngIf="item.stateSpan">{{ item.state }}</td>
      <td [attr.rowspan]="item.countySpan" *ngIf="item.countySpan">{{ item.county }}</td>
      <td>{{ item.item }}</td>
    </tr>
</table>
export class AppComponent  {
  data = [
    { state: 'MN', county: '1', item: 0.297 },
    { state: 'MN', county: '1', item: 0.04 },
    { state: 'MN', county: '3', item: 0.14 },
    { state: 'CA', county: '2', item: 0.019 },
    { state: 'MN', county: '1', item: 0.0374 }, 
    { state: 'CA', county: '2', item: 0.037 },
    { state: 'CA', county: '3', item: 0.14 }
  ];

  dataExt: any[] = [];

  constructor() {
    this.processData();
  }

  private processData() {
    const statesSeen = {};
    const countiesSeen = {};

    this.dataExt = this.data.sort((a, b) => {
      const stateComp = a.state.localeCompare(b.state);
      return stateComp ? stateComp : a.county.localeCompare(b.county);
    }).map(x => {
      const stateSpan = statesSeen[x.state] ? 0 :
        this.data.filter(y => y.state === x.state).length;

      statesSeen[x.state] = true;

      const countySpan = countiesSeen[x.state] && countiesSeen[x.state][x.county] ? 0 :
        this.data.filter(y => y.state === x.state && y.county === x.county).length;

      countiesSeen[x.state] = countiesSeen[x.state] || {};
      countiesSeen[x.state][x.county] = true;

      return { ...x, stateSpan, countySpan };
    });
  }
}

The resulting table can be viewed here:

https://i.stack.imgur.com/oq3va.png

Answer №5

Maybe try a solution similar to the following:

interface Data {
  category?: string;
  subcategory?: string;
  value: number;
}

private organizeData(inputData: any[]): Data[] {
  // Arrange data alphabetically
  const sortedData = inputData.sort((a, b) => {
    if(a.category < b.category) { return -1; }
    if(a.category > b.category) { return 1; }
    return 0;
  });

  // Keep only one entry per distinct category value
  return sortedData.map((element, index) => {
    if (index === 0 || sortedData[index-1].category !== element.category) {
      return element;
    } else {
      return {
        item: element.item
      };
    }
  });
}

This approach should have a time complexity of `O(log n)` and provide results in the format shown below:

[
  { category: 'A', subcategory: '1', value: 0.123 },
  { value: 0.456 },
  { category: 'B', subcategory: '2', value: 0.789 },
  ...
]

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

Is there a way to send an array of objects to my Express / SQL Server in react?

I am currently facing a challenge with passing an array of objects to my back end (SQL Server). Below is the specific array of objects: console.info(results) Here is the array of objects named results: [0] [ [0] { [0] answer: 'bmbm,nn', [ ...

Using an iframe to access HTTP content from an HTTPS website

Currently, I am facing an issue with a page that contains an iframe loading an HTML template from an Amazon AWS S3 bucket. Within the iframe, my goal is to take the link of the parent window and add additional parameters. For example, let's say my pa ...

when the AJAX request has a specific condition

When making an AJAX request, I want to pass data only if a certain condition is met. The "data: testobj" line represents a JSON object that will return {"name":"tom"} $.ajax({ type: "POST", url: 'test.asp', data: testobj, succes ...

Incorporate audio playback on image click using JavaScript, with the feature to automatically pause the playback if multiple images are playing simultaneously

<img class="cl" src="photo/198.jpg"/></br> <audio class="cs" controls> <source src="audio/198 banu nagamothu.mp3" type="audio/mpeg"> </audio> I prefer not to have audio controls initially, but when the image i ...

The method for connecting the width of a panel to the height of the viewport in Ext Js

I'm looking to automate the adjustment of a panel's height based on the viewport's height using Ext js. Although achievable using listeners, I believe utilizing Ext js variable binding would be more efficient. However, I've encountered ...

Is there a way to adjust the height of one div based on the height of another div in Bootstrap?

I am experimenting with a Bootstrap example featuring a table in the left column and 4 columns in 2 rows in the right column. Check out the code below: <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css ...

Issue encountered with Typescript and Mongoose while operating within a Kubernetes cluster environment with Skaffold configuration

Here is the code snippet, const userSchema = new mongoose.Schema({ email: { type: String, required: true, }, password: { type: String, required: true, }, }); console.log(userSchema); userSchema.statics.build = (user: UserAttrs) =& ...

Initializing a table with data will only function properly when a breakpoint is set

Using the bootstrap-table library, I initialize a table with data fetched via ajax request. var arr = []; var getRows = function () { $.ajax({ type: "GET", url: hostUrl, contentType: "app ...

Troubleshooting regex validation issues in a JSFiddle form

Link to JSFiddle I encountered an issue with JSFiddle and I am having trouble figuring out the root cause. My aim is to validate an input using a regex pattern for a person's name. $("document").ready(function() { function validateForm() { var ...

Angular2 app hosted on firebase displaying an empty page

After developing an angular2 app, I followed the procedure for Firebase hosting. However, when I try to access my app, it is showing a default page instead. I would appreciate any help or guidance on this issue. ...

Implement jQuery to close multiple div elements

My dilemma involves a series of divs with different manufacturer IDs listed as follows: manufacturer[1], manufacturer[2], manufacturer[3], ... etc ... In attempting to create a JavaScript for loop to hide each div, I discovered that it was not achievab ...

How can I receive user input in JavaScript while incorporating three.js?

I am currently exploring the functionalities of this three.js example () and I am interested in enabling users to input specified X, Y, and Z positions for boxes dynamically. Initially, I considered utilizing a JavaScript prompt like below: var boxes = p ...

ES6 Update: Manipulating Nested Arrays with JavaScript

I have the following list of items: [ { idItem: "1", name: "apple", itemLikes: [{ id: "1", idItem: "1" }] } ] My goal is to simply add a new object to the itemLikes array. Here is my ...

Utilizing jQuery to extract the `h1` element from a dynamically loaded external page within the

I am facing a challenge in selecting an h1 element from a remote page that I have loaded into $(data). Despite several attempts, I am struggling to write the correct code. When I use this code: console.log($(data)); The output is as follows: [text, meta ...

Empty results in NgRx Parameterized Selector

Having trouble implementing a parameterized query in NgRx and receiving empty results. Check out the StackBlitz version of the code here: https://stackblitz.com/edit/ngrx-parameterized-query Update to Reducer Code export const userAdapter = createEntity ...

Why won't the code for detecting the DEL key in a textarea work on my computer?

I've been trying to detect when a user presses the Delete key and came across this helpful tutorial here The code worked flawlessly on jsfiddle.net, you can check it out here- http://jsfiddle.net. However, when I copied the same code to my local comp ...

When attempting to use nodejs's collection.find() method with MongoDB, no response is received

I have been struggling to develop a node.js script that can retrieve only the records with a specific string key-value pair from a MongoDB collection containing around 30,000 documents. When I run this query on my MongoDB server, it gives me the exact res ...

Following the upgrade of Angular, the webpack module source-map-loader is encountering an error: "this.getOptions is not a function"

Currently in the process of constructing my angular project using webpack alongside source-map-loader to extract source maps, like this: module.exports = { // ... module: { rules: [ { test: /\.js$/, enforce: "pre&quo ...

Attempting to run driver.execute_script(gooogletag but no response was received

I have been attempting to receive responses in the console from the browser when running googletag parameters with Selenium, but unfortunately I have not been successful. Even after trying .execute_async_script('googletag.pubads()') and putting ...

What is the best way to style radio boxes in HTML to resemble checkboxes and display X's when selected?

I'm looking to create a form with radio boxes that resemble checkboxes and display a glyphicon x when selected. I've experimented with various solutions such as: input[type="radio"] { -webkit-appearance: checkbox; /* Chrome, ...