Transform an item into a different item using TypeScript

I have a database result showing in the file attached: https://i.sstatic.net/K96Ps.png

The input data I have is as follows:

const input = [
    {
        PurchaseInvoice_id: '8e54a096-568b-48d9-8461-826be53a32da',
        PurchaseInvoicePosition_id: '44edfd7f-bc9e-4155-ad5c-5dace9c7c31a',
        ProductStock_id: '0a701dbc-2661-4d67-b764-632cfb67334f',
    },
    ...

I am looking to convert the input object into an output object like this:

const output = [
    {
        PurchaseInvoice_id: '8e54a096-568b-48d9-8461-826be53a32da',
        PurchaseInvoicePosition_ids: [
            {
                PurchaseInvoicePosition_id: '44edfd7f-bc9e-4155-ad5c-5dace9c7c31a',
                ProductStock_ids: [...],
            },
            ...
        ],
    },
];

This transformation should look similar to the image provided when folded: https://i.sstatic.net/Q2Gyi.png

As a PHP developer, I find it challenging to achieve this conversion in JavaScript. I have tried several approaches without success, such as iterating over the input object multiple times.

The input object may contain multiple PurchaseInvoice_id entries connected by PurchaseInvoicePosition_id and ProductStock_id.

Answer №1

Please utilize the Array.reduce method to solve your inquiry.

Procedure

  • Iterate through the provided array.
  • Determine if there exists a node with the current PurchaseInvoice_id in the accumulator.
  • If it is not present, append the current node to the accumulator using the specified format (Refer to Section 1 in the code comment).
  • If it is found (as indicated in Section 2 of the code comment), ascertain whether there is a node with the current PurchaseInvoicePosition_id in the PurchaseInvoicePosition_ids of the identified node.
  • If this node is not discovered (Section 3 in code comment), include the current PurchaseInvoicePosition_id and ProductStock_id in the accumulator.
  • If it is located (as described in Section 4 of the code comment), insert the ProductStock_id into the ProductStock_ids of the located node.

View Working Example Here: Fiddle Link

const input = [
  { PurchaseInvoice_id: '8e54a096-568b-48d9-8461-826be53a32da', PurchaseInvoicePosition_id: '44edfd7f-bc9e-4155-ad5c-5dace9c7c31a', ProductStock_id: '0a701dbc-2661-4d67-b764-632cfb67334f', },
  // Include more data here as needed
];
// Implementation logic goes here

Answer №2

Let's introduce a function called nestedGroups(), which operates on an array of objects named arr, and a sequence of keys such as key1, key2, key3, etc., belonging to the elements in arr. When calling

nestedGroups(arr, key1, key2, key3)
, it will result in an object where the key is the pluralized version (by appending "s") of key1, and the corresponding value contains groupings for subsequent keys, and so forth.

const ng = nestedGroups(
    [{ a: 1, b: 2 }, { a: 1, b: 3 }, { a: 4, b: 5 }, { a: 4, b: 6 }],
    "a", "b"
);
/* const ng: { as: { a: number; bs: { b: number; }[]; }[]; } */

console.log(ng)
/* {
  "as": [
    {"a": 1, "bs": [{"b": 2}, {"b": 3}]},
    {"a": 4, "bs": [{"b": 5}, {"b": 6}]}
  ]
} */

An implementation of nestedGroups() is shown below:

// implementation
function nestedGroups(arr: Record<string, any>[], ...keys: string[]): any {
    const [firstKey, ...restKeys] = keys;
    if (firstKey === undefined) return {};
    const retmap = new Map<any, any>();
    arr.forEach(v => {
        const val = v[firstKey];
        if (!(retmap.has(val))) retmap.set(val, []);
        retmap.get(val)!.push(v);
    });
    return {
        [firstKey + "s"]: Array.from(retmap.entries()).map(([k, v]) =>
            ({ [firstKey]: k, ...nestedGroups(v, ...restKeys) }))
    }
}

The function above recursively calls itself until all keys are processed, grouping values accordingly. To fully support TypeScript, we can define a type NestedGroups<T, K> that represents the return value of nestedGroups(arr, ...keys):

type NestedGroups<T extends Record<K[number], Primitive>, K extends string[]> =
    K extends [infer F, ...infer R] ? F extends string ? { [P in \`\${F}s\`]:
        Array<Pick<T, F> & NestedGroups<T, Extract<R, string[]>> extends
            infer O ? { [P in keyof O]: O[P] } : never>; } : never : {};

type Primitive = null | undefined | number | bigint | string | boolean | symbol;

Lastly, we declare the overloaded function signature for nestedGroups() with strong typing:

// call signature
function nestedGroups<T extends Record<K[number], Primitive>, K extends string[]>
    (arr: T[], ...keys: K): NestedGroups<T, K>;

A sample test using your input data can be seen below:

const r = nestedGroups(input, "PurchaseInvoice_id", "PurchaseInvoicePosition_id", "ProductStock_id");

const output = r.PurchaseInvoice_ids;

/* const output: {
    PurchaseInvoice_id: string;
    PurchaseInvoicePosition_ids: {
        PurchaseInvoicePosition_id: string;
        ProductStock_ids: {
            ProductStock_id: string | null;
        }[];
    }[];
}[] */

console.log(output);
/*
 [{
  "PurchaseInvoice_id": "8e54a096-568b-48d9-8461-826be53a32da",
  "PurchaseInvoicePosition_ids": [
    {
      "PurchaseInvoicePosition_id": "44edfd7f-bc9e-4155-ad5c-5dace9c7c31a",
      "ProductStock_ids": [
        {"ProductStock_id": "0a701dbc-2661-4d67-b764-632cfb67334f"},
        {"ProductStock_id": "15278807-794a-4727-9bcb-f7f68dfb4d41"},
        {"ProductStock_id": "0ac9fcd7-73f0-47b1-8fbc-3948863e7a89"}
      ]
    },
    // Additional data follows...
]
*/

This exemplifies how to leverage strong types in nestedGroups(), ensuring correctness and offering detailed insights into the resulting structure.

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

Java method for arranging array elements

Can someone help me with the most efficient method to randomly shuffle elements in a sorted array using Java? Thank you! ...

Check with jQuery if there is only one item in the array

Hey there! I'm trying to work on a jQuery function that will take the value of a checkbox and use it as an identifier to show or hide a list. The issue I'm facing is that I can't seem to figure out how to add a condition to only display X wh ...

Potential Null Object in Typescript Mongoose: A Concern

Encountering an issue while attempting to locate my user model: Object is possibly 'null'. I would like to find a solution that does not involve suppressing TypeScript's strict rule. const { email, password } = req.body; const user = awai ...

Tips for extracting and structuring strings from unstructured CSV data using JavaScript?

From a public source, I've extracted a string of allergy data: Cedar 679 gr/m3 High, Grass 20 gr/m3 Medium, Trees 80 gr/m3 Medium, Molds Low. Although the number of items may vary, the standard format for trees and grasses is consistent, with allerge ...

Having trouble with Append() function not functioning properly within the if statement nested inside a .each loop in jQuery

Looking to add text after the 4th element in a ul li? I tried using the append function, but it's appending before every li element. Here's the HTML: <ul class="gallery"> <li><a href="#">Some Text</a> ...

Navigational buttons for a JavaScript/HTML questionnaire webpage

For my survey page, I'm utilizing Bootstrap nav-tab to create questionnaire tabs. However, I'm encountering an issue with the code snippet below that is meant for navigating between previous and next tabs. Despite implementing the code provided, ...

Unable to start dat.GUI in Three.js

After running my code, I encountered an error on this line: var gui = new dat.GUI(); The error message reads: 'Unable to get the 'getItem' property null reference or undefined.' Despite importing the necessary library, I am unsure of ...

Issue: StaticInjectorError(DynamicTestModule)[CityService -> Http]: Http provider not found

I am working on a service that retrieves all cities from a web service. @Injectable() export class CityService { constructor(private http: Http, private router: Router, private auth: AuthService) { } public getAllCity(): Observable<City[]> { ...

Attempting to persist a nested document in MongoDB using mongoose within a Nodejs environment

I attempted to save an address as a nested document in the following format When sending data from the client side, it was formatted like this: const address = JSON.stringify( { addressline1:form.addressline1.value, addressline2:form.addressline2.value, c ...

Searching for a specific word within a given string using a loop

Currently, I'm developing a 'for' loop to search for my name, Andrew, in a given text and store the occurrences in an array. However, there seems to be an issue with the implementation. /*jshint multistr:true */ var text = ("Andrew is real ...

Adding React components dynamically through code

My journey with React is just beginning, and I am faced with a challenge of adding new components to an existing setup. However, I'm unsure of the process. Here is my current scenario: I have a list of Seances along with a button to add more: Seanc ...

"Exploring the power of AJAX, manipulating strings with JavaScript, and building

In the process of creating a pyramid web app, I am currently working on integrating the JavaScript markdown editor EpicEditor for editing markdown files. $.ajax({ url: "{{ request.resource_url(context) }}raw_markdown", context: document.body, ...

Using Javascript with AngularJS to integrate with the Leaflet library for translation

I come across the same kind of statement every time I search the internet or read about leaflet, for example : var map = L.mapbox.map('map', { zoomControl:false }); What does this mean? And how can I incorporate it into Angularjs? P.S. : html ...

Creating Typescript: Defining the Type of Object Key for a Generic Type

I've created a custom hook with a generic type to define the return type of the hook. Below is an example of the code I wrote for this custom hook: type Return<T> = [T, (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void, ...

Vue.js: Redundant CSS classes (CSS components) are created when importing the same CSS file

I am currently developing an App using Vue.js and CSS Components. Within my Vue components, I have noticed that some share similar styling. In my Hello.vue component: <template> <div :class="$style.title">Hello, World</div> </templ ...

What is the process for validating a json file using jsonschema?

Help needed with validating JSON input - my_json. An exception was expected due to discrepancy in keys. Any suggestions on how to validate this JSON data? import json from jsonschema import validate # Define the expected schema for the JSON. schema = { ...

Multi-validation for user input in JavaScript

When working with JavaScript, I have encountered a challenge where I need to validate an input that can only accept either 11 numeric characters or 17 alphabetic characters. Individually, I am able to check for one of these conditions but struggling to im ...

Is it possible to invoke Bootstrap modal functions without using jQuery?

I'm in the process of removing jQuery dependencies from my app, but I still rely on Bootstrap. Is there a way to trigger modal functions like $('#myModal').modal('show') without using jQuery now? ...

Determine the quantity of JSON entities

After spending hours searching, I have finally decided to post this question. I am facing a challenge with a variable named $responses that outputs encoded JSON objects as shown below: [ { "notification_id": 4936, "notification_title": "Bridg ...

When attempting to deploy my app, I encountered a CORS error with Nest.js

Currently, I am in the process of building a Nest.js - React.js application. However, I am encountering a cors error despite having cors enabled in my main.ts file of Nest.js. While my application functions smoothly on localhost and an IP address in produ ...