Combining and consolidating data from state using Angular with ngrx

I've been struggling to come up with a way to create a grouped and summarized array of values (to be used with ngFor) from a list of objects, but I just can't seem to get it right. The data, which is a subset of my state, is structured like this:

[{name: "A", value: 1, desc: 'something irrelevant'}, 
 {name: "A", value: 3, desc: 'also other properties'}, 
 {name: "B", value: 2, desc: 'etc.'}, 
 {name: "B", value: 5, desc: 'etc.'}]

The desired result that I'm aiming for is something similar to (note the different data type):

[{name: "A", value: 4}, {name: "B", value: 7}]

In essence, I want to identify the unique "names" and calculate the total "value" for all objects with that name, in a format suitable for ngFor | async.

My solution that I got close to working is:

       this.aggregates:Observable<any[]> = this.store
        .select(state => state.valuesList)
        .map(valuesList => valuesList.sort((a,b) => {return (a.name > b.name) ? 1 : ((b.name > a.name) ? -1 : 0);} }))
        .flatMap(valuesList => valuesList)
        .map(value => value.name)
        .distinct();

I'm fine with this start, yet the issue arises when I try to use toArray(). If I skip it, Typescript throws an error "Type string is not assignable to type any[]"; when I include toArray() after distinct(), no results are returned upon subscribing.

What am I missing here? Should I consider moving this logic to the reducer, even though I'm uncertain if I can change the type of objects returned by different actions within the same reducer? Any guidance would be greatly appreciated.

UPDATE: I would greatly appreciate a functioning implementation of groupBy(), as it seems to perfectly fit this scenario.

Answer №1

If you want to achieve the desired outcome, you can utilize the groupBy method. However, it is important to apply the groupBy operator to an observable that is derived from the list since groupBy expects an observable containing items.

In the code snippet below, the variable slice is essentially equal to

this.store.select(state => state.valuesList)

const slice = Rx.Observable.of([
  { name: "A", value: 1, desc: "something irrelevant" },
  { name: "A", value: 3, desc: "also other properties" },
  { name: "B", value: 2, desc: "etc." },
  { name: "B", value: 5, desc: "etc." }
]);

const grouped = slice.concatMap(list => Rx.Observable
  .from(list)
  .groupBy(item => item.name)
  .mergeMap(group => group
    .reduce((total, item) => total + item.value, 0)
    .map(total => ({ name: group.key, value: total }))
  )
  .toArray()
);

grouped.subscribe(value => console.log(value));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://unpkg.com/rxjs@5/bundles/Rx.min.js"></script>

Answer №2

Here's an example snippet of code:

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/from';
import 'rxjs/add/operator/map';

function processValues() {
    const data = [
        { name: "C", value: 3, desc: 'other details' },
        { name: "A", value: 1, desc: 'irrelevant info' },
        { name: "B", value: 2, desc: 'etc.' },
        { name: "A", value: 3, desc: 'other properties' },
        { name: "B", value: 5, desc: 'etc.' }
    ];

    const dataStream = Observable.from(Array(100).fill(data));

    const modifiedData: Observable<any[]> = dataStream
    .map((valuesList) => valuesList
        .map((x) => ({ name: x.name, value: x.value }))
        .sort((a, b) => a.name.localeCompare(b.name))
        .reduce((pre, cur) => {
            const len = pre.length - 1;

            if (pre[len] && pre[len].name === cur.name) {
                pre[len].value += cur.value;
                return pre;
            }
            pre[len + 1] = cur;
            return pre;
        }, [])
    );

    return modifiedData;
}

processValues().subscribe((result) => {
    console.dir(result, { depth: null });
});

The processed output is demonstrated below:

[ { name: 'A', value: 4 },
  { name: 'B', value: 7 },
  { name: 'C', value: 3 } ]
[ { name: 'A', value: 4 },
  { name: 'B', value: 7 },
  { name: 'C', value: 3 } ]
[ { name: 'A', value: 4 },....

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

Why is it that I am able to invoke my Redux action creators from the componentWillMount() method, but not from my event handler function?

I'm currently developing an application using React, Redux, and TypeScript. My Redux store is all set up with initial state and it's working fine in populating my components. Now, I am in the process of connecting a form that will trigger my act ...

Is it possible to make the 'keyof' property optional?

Illustrate an interface in the following way interface Properties { apple?: string banana?: string cherry?: string date: string } Executing this code works as expected type Sample1 = { [P in keyof Properties]: Properties[P] } const s1: Sample1 ...

Simulating TypeDI service behavior in Jest

My current setup includes Node with TypeScript, TypeDI and Jest. I've been working on creating services that have dependencies on each other. For example: @Service() export class MainService{ constructor(private secondService: SecondService){} public ...

Exclude the use of ':any' in React component

Currently, I am new to React and facing a challenge in sending a variable and function to my component. I understand that using :any is not the best practice, so I am seeking a better solution. I am working on creating a modal and passing data to my compo ...

Error encountered when utilizing ReactiveForms and NgbModal: ExpressionChangedAfterItHasBeenCheckedError

I am working with a reactive form, and I want to display a confirmation modal when the user presses the ESC key in an input before hiding the form. However, upon showing the modal, I encounter the following exception in the console: ERROR Error: Express ...

Guide to creating unit tests using Mocha, JsDom, and Angular

I am currently working on setting up a unit test project using Mocha and Angular. In order to execute it in the console with jUnit report for Jenkins, I have included JsDom in my project. However, I am facing an issue with loading my Angular application. ...

Achieve a main-menu container with a full width dropdown using Bootstrap 4

I want the dropdown to start from the initial position for all menus and maintain a width of 600px. Even if I attempt to open the dropdown for "Main Menu 2" or "Main Menu 3" or "Main Menu 4", the dropdown should always start from the position of "Main Men ...

Using Angular 7 with FileSaver.js

I am currently facing a situation where I need to download data in the form of an Excel file. My setup involves Angular 7 for the UI and a Java Spring REST API service. The REST API is responsible for generating the Excel file and sending it back to Angula ...

Encountered an issue trying to access undefined properties while reading 'PP'

I am trying to showcase the data retrieved from my JSON file. Here is a glimpse of the information stored in the JSON => Within DTA > PP , I am specifically interested in displaying the variable NOMFAMILLE. An error message has caught my attentio ...

Utilizing Next.js to Import a Function from a Path Stored within an Environment Variable

I'm having trouble importing a function whose path is stored in an environment variable Current import statement: import { debug } from '../lib/site-A/utils' Expected import statement: import { debug } from process.env.DEBUG_PATH In my ...

Angular universal triggers an "Error at XMLHttpRequest.send" issue

After updating my project to Angular 10 and incorporating angular universal, I encountered a strange error. While the application builds without any issues, I face an error when trying to run it on my development environment: ERROR Error at XMLHttpReque ...

typescript function intersection types

Encountering challenges with TypeScript, I came across the following simple example: type g = 1 & 2 // never type h = ((x: 1) => 0) & ((x: 2) => 0) // why h not never type i = ((x: 1 & 2) => 0)// why x not never The puzzling part is w ...

The error message "Property 'listingSub$' is not initialized in the constructor and is not definitely assigned" needs to be addressed and fixed in the code

import { Subscription } from "rxjs"; @Component({ selector: 'app-listing-detail', templateUrl: './listing-detail.component.html', styleUrls: ['./listing-detail.component.scss'] }) export class ListingDetailCo ...

Is it possible to utilize instanceof to verify whether a certain variable is of a class constructor type in TypeScript?

I am currently facing an issue with a function that takes a constructor as a parameter and creates an instance based on that constructor. When attempting to check the type of the constructor, I encountered an error. Below are some snippets of code that I ...

Set every attribute inside a Typescript interface as non-mandatory

I have defined an interface within my software: interface Asset { id: string; internal_id: string; usage: number; } This interface is a component of another interface named Post: interface Post { asset: Asset; } In addition, there is an interfa ...

Sophisticated method for retrograding every Angular component for AngularJS

I am in the process of transitioning my app from angularjs to angular. As I create new angular components, I am looking for a more streamlined way to import and downgrade them automatically. Is there an elegant solution for this? Here is my current code: ...

The FormGroup instance does not return any input method when using the get input function

I am facing an issue where a custom error message should only display if the input for foo is invalid. However, it seems like the line of code myForm.get('foo') is returning null. Below is the simplified version of component.html: <form [for ...

Image uploads are a challenge for Angular

I'm facing an issue with my Angular app while trying to upload an image to the server (node backend). Despite trying various methods found online, I have encountered the same problem consistently - the server does not receive the image. Interestingly, ...

Contrast between sourcing a file from the current directory versus from the node_modules folder

Why does the typescript compiler accept importing from a JS file in the local directory but raises an error when importing from node_modules? Code: import { t2 } from "./t1.js" t2.hello(); import { mat4 } from "./node_modules/gl-matrix/esm ...

Tips on transferring data to a parent component without dismissing the Angular Material dialog when a button is clicked

Is there a way to transfer data from a popup (angular material dialog) to its parent component when a button is clicked, all without closing the popup window? I'm stumped on how to accomplish this task. If anyone knows a solution, please lend me a ha ...