Using TypeScript, include elements to an array when a specific criterion is met

I am trying to update the array finalObject.members by using values from another array called allParticipants. The structure of the second array (allParticipants) is as follows:

allParticipants = [
    { 
        uid:"mem1_100_00",
        member: "mem1",
        tontine: "100"
        total: 785
    },
    { 
        uid:"mem1_100_01",
        member: "mem1",
        tontine: "100"
        total: 800
    },
    { 
        uid:"mem1_200_00",
        member: "mem1",
        tontine: "200"
        total: 1000
    },
    {
        uid:"mem2_100_00",
        member: "mem2",
        tontine: "100"
        total: 200
    },
    { 
        uid:"mem2_200_00",
        member: "mem2",
        tontine: "200"
        total: 7850
    },
    { 
        uid:"mem2_200_01",
        member: "mem2",
        tontine: "200"
        total: 5000
    },
    { 
        uid:"mem2_200_02",
        member: "mem2",
        tontine: "200"
        total: 1600
    },
    { 
        uid:"mem3_100_00",
        member: "mem3",
        tontine: "100"
        total: 150
    },
    { 
        uid:"mem3_100_01",
        member: "mem3",
        tontine: "100"
        total: 0
    },
    { 
        uid:"mem3_200_00",
        member: "mem3",
        tontine: "200"
        total: 2500
    }

]

The updated array (finalObject.members) is expected to have the following structure after the updates:

finalObject.members = [
    { 
        uid: "mem1",
        tontines: {
            100:[
                {
                    uid: "mem1_100_00",
                    total:785
                },
                {
                    uid: "mem1_100_01",
                    total:800
                },
            ],
            200:[
                {
                    uid: "mem1_200_00",
                    total:1000
                }
              ]
        }
    },
    { 
        uid: "mem2",
        tontines: {
            100: [
                {
                    uid: "mem2_100_00",
                    total: 200
                }
            ],
            200:[
                {
                    uid: "mem2_200_00",
                    total: 7850
                },
                {
                    uid: "mem2_200_01",
                    total: 5000
                },
                {
                    uid: "mem2_200_02",
                    total: 1600
                }
            ]
        }
    },
    { 
        uid: "mem3",
        tontines: {
            100: [
                {
                    uid: "mem3_100_00",
                    total: 150                
                },
                {
                    uid: "mem3_100_01",
                    total: 0                
                }
            ],
            200:[
                {
                    uid: "mem3_200_00",
                    total: 2500        
                }
            ]
        }
    }
]

The code I have written for this process is as follows:

const sizMem    = finalObject.members.length;
const sizPartp  = allParticipants.length;

for(let idx1=0; idx1<sizPartp; idx1++){
    let partP = allParticipants[idx1]
    for(let idx2=0; idx2<sizMem; idx2++){
        let memP = finalObject.members[idx2];
        if(partP.member.localeCompare(memP.uid) == 0){
            finalObject.members[idx2].tontines[partP.tontine].push({
                uid:     partP.uid,
                total:   partP.total,
            })
            break
        }
    }
}

However, the output I am getting seems to be incorrect. It is adding all elements for each member instead of adding only the new element to the corresponding member. I have double-checked the if conditions and everything seems to be correct. The insertion is only happening when the member property of Participant matches the uid property of a member. Yet, the new element is being added everywhere!

What could be the issue in my code?

Answer №1

What Mistakes Did I Make?

  1. Your code is overly complex. Attempting to handle everything at once with 2 nested loops. Using indexes instead of (for...of) when they are not necessary.

  2. A more specific issue: you only have ONE push, where you need to add

    • new members
    • new tontines for a member
    • new participations for a tontine for a member

Organizing your data by member-id first, then tontine-id, can simplify things.

var participations = {}
for (let  p of allParticipations) {
    let member = p.member
    if ( ! (member in participations)) 
          participations[member] = { "member": member, "tontines" : {} }
    }  
    let tontine = p.tontine
    if ( ! (tontine in participations[member].tontines)) {
          participations[member].tontines[tontine] = [] 
    }
    participations[member].tontines[tontine].push( { "uid": p.uid, "total": p.total })
}

This results in:

{ 
  mem1: { member: 'mem1', tontines: { '100': [Array], '200': [Array] } },
  mem2: { member: 'mem2', tontines: { '100': [Array], '200': [Array] } },
  mem3: { member: 'mem3', tontines: { '100': [Array], '200': [Array] } } 
}

Indexing eliminates the need for search loops. It makes it easier to construct the desired output from the participations variable.


EDIT: This intermediate data structure can be simplified into a doubly indexed dictionary of lists

var participations = {}
for (let p of allParticipations) {
    if ( ! (p.member in participations)) participations[p.member] = {}
    if ( ! (p.tontine in participations[p.member])) participations[p.member][p.tontine] = [] 
    participations[p.member][p.tontine].push({ "uid": p.uid, "total": p.total })
}

Result:

{ 
  mem1: { '100': [ [Object], [Object] ], '200': [ [Object] ] },
  mem2: { '100': [ [Object] ], '200': [ [Object], [Object], [Object] ] },
  mem3: { '100': [ [Object], [Object] ], '200': [ [Object] ] } 
}

EDIT 2: In a more functional style, a general-purpose multi-level indexing function can be useful:

var x = indexBy(allParticipations,
                [ p => p.member, p => p.tontine ],    // keys
                p => ({ "uid"   : p.uid,               // items
                        "total" : p.total
                     })
           )

Source code:

function indexBy(data, extractors, finalizer) {
    let all_but_last = extractors.slice(0, extractors.length-1)
    let last = extractors[extractors.length-1]

    let result = {} 
    for (let item of data) {
        let node = result
        for (let extractor of all_but_last) {
            let key = extractor(item)
            if ( !(key in node)) node[key] = {}  // indexing all the way down
            node = node[key]
        }
        let key = last(item)
        if ( !(key in node)) node[key] = []   // last level is an array
        node[key].push(finalizer(item))
    }
    return result   
}

Answer №2

If you're looking for a handy library without any dependencies, consider checking out blinq. It's perfect for handling various transformations.

const output = blinq(allParticipants)
  .groupBy(x => x.member)
  .select(g => ({
    uid: g.key,
    member: Object.fromEntries(
      g
      .groupBy(x => x.tontine)
      .select(gg => [
        gg.key,
        gg.select(x => ({
          uid: x.uid,
          total: x.total
        })).toArray()
      ])
      .toArray()
    )
  }))
  .toArray();

const {
  blinq
} = window.blinq
const allParticipants = [{
    uid: "mem1_100_00",
    member: "mem1",
    tontine: "100",
    total: 785
  },
  {
    uid: "mem1_100_01",
    member: "mem1",
    tontine: "100",
    total: 800
  },
  {
    uid: "mem1_200_00",
    member: "mem1",
    tontine: "200",
    total: 1000
  },
  {
    uid: "mem2_100_00",
    member: "mem2",
    tontine: "100",
    total: 200
  },
  {
    uid: "mem2_200_00",
    member: "mem2",
    tontine: "200",
    total: 7850
  },
  {
    uid: "mem2_200_01",
    member: "mem2",
    tontine: "200",
    total: 5000
  },
  {
    uid: "mem2_200_02",
    member: "mem2",
    tontine: "200",
    total: 1600
  },
  {
    uid: "mem3_100_00",
    member: "mem3",
    tontine: "100",
    total: 150
  },
  {
    uid: "mem3_100_01",
    member: "mem3",
    tontine: "100",
    total: 0
  },
  {
    uid: "mem3_200_00",
    member: "mem3",
    tontine: "200",
    total: 2500
  }
];
const output = blinq(allParticipants)
  .groupBy(x => x.member)
  .select(g => ({
    uid: g.key,
    member: Object.fromEntries(
      g
      .groupBy(x => x.tontine)
      .select(gg => [
        gg.key,
        gg.select(x => ({
          uid: x.uid,
          total: x.total
        })).toArray()
      ])
      .toArray()
    )
  }))
  .toArray();
console.log(JSON.stringify(output, null, 2));
<script src="https://cdn.jsdelivr.net/npm/blinq"></script>

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

Initiate the input change event manually

Struggling with creating a custom counter input component where the input value is controlled by custom increment/decrement buttons. Desired output: https://i.sstatic.net/oYl1g.png Content projection will be used to expose the input for form usage and a ...

Having trouble accessing an element after parsing JSON using jQuery, ending up with an "

Here is a snippet of code with a text and a button: <div id="divtest"> <h1> Bla </h1> </div> <button id="dugme1"> dugme </button> When the user clicks the button, the following script will be executed: $("#dugme1").cl ...

Issues with type errors in authentication wrapper for getServerSideProps

While working on implementing an auth wrapper for getServerSideProps in Next.js, I encountered some type errors within the hook and on the pages that require it. Below is the code for the wrapper along with the TypeScript error messages. It's importan ...

How can I retrieve routing parameters in a Vue.js/Nuxt/TypeScript app?

In the process of developing my website based on the Nuxt TypeScript Starter template, I've encountered a challenge. Specifically, I have created a dynamically routed page named _id.vue within my pages folder and am looking to access the id property i ...

Can a single data type be utilized in a function that has multiple parameters?

Suppose I have the following functions: add(x : number, y : number) subtract(x : number, y : number) Is there a way to simplify it like this? type common = x : number, y : number add<common>() This would prevent me from having to repeatedly define ...

Tips on how to modify the session type in session callback within Next-auth while utilizing Typescript

With my typescript setup, my file named [...next-auth].tsx is structured as follows: import NextAuth, { Awaitable, Session, User } from "next-auth"; // import GithubProvider from "next-auth/providers/github"; import GoogleProvider from ...

Populating the array by calculating the average values

I'm encountering an issue in my JavaScript code. I have a situation where I must fill gaps in an array with the averages of the surrounding values. Let me provide an example: Array: 1, 2, 3, ,4, 5 In this case, I would need to fill the gap with the ...

When an object in Typescript is clearly a function, it throws a 'cannot invoke' error

Check out this TypeScript code snippet Take a look here type mutable<A,B> = { mutate: (x : A) => B } type maybeMutable<A,B> = { mutate? : (x : A) => B; } const myFunction = function<A,B>(config : A extends B ? maybeMutab ...

Streamline imports with Typescript

Is there a way to import modules with an alias? For example, I have an angular service in the folder common/services/logger/logger.ts. How can I make the import look like import {Logger} from "services" where "services" contains all my services? I've ...

The underscore convention for defining members in Typescript allows for clear and concise

Let's talk about a class called Email class Email { private _from: string; private _to: Array<string>; private _subject: string; } When an email object is created, it will look something like this: { _from:'', _to:'&apo ...

What is a way to organize data with an array element in MongoDB that does not involve utilizing the unwind method

Within this sample data, there is a userId and an array called "watchHistory" which contains a list of videos watched by the user: { "_id": "62821344445c30b35b441f11", "userId": 579, "__v" ...

Retrieving data from a JSON file with fields scattered throughout multiple dictionaries

While working on extracting data from a nested JSON file in Python 3.8, I encountered a KeyError related to the 'extended_tweet' key: extended_tweet = data[str(i)]['extended_tweet']['full_text'] KeyError: 'extended_tweet ...

In Python, the size of the bytearray object does not align with the size of the corresponding bytes object

Here's some code I've been working with: some_buf = build_buffer() # generates a bytearray containing around 40 bytes align_buf(some_buf) # ensures alignment to a 16-byte boundary do_something(some_buf[16:32]) # This function requires a bytes ob ...

Determining the type of <this> in an Object extension method using TypeScript

I am attempting to incorporate a functionality similar to the let scope function found in Kotlin into TypeScript. My current strategy involves using declaration merging with the Object interface. While this approach generally works, I find myself missing ...

Merging object keys and values from JSON arrays based on their keys, using JavaScript

Is there a way to merge object keys' values from JSON arrays based on their key? json1 = [ {key:'xyz', value:['a','b']}, {key:'pqrs', value:['x','y']} ] json2 = ...

combine two php arrays that have different lengths by their values

Greetings! I have a query regarding merging two PHP arrays of different lengths based on identical key-value pairs within both arrays. Despite searching through numerous similar posts, none seemed to address my specific requirements. The first array is ge ...

TypeScript generic types allow you to create reusable components that

function genericIdentity<T>(arg: T): T { return arg; } let myGenericIdentity: <U>(arg: U) => U = genericIdentity; I see that the 'genericIdentity' function is accepting an argument of a generic type. However, I am unsure about ...

Exploring TypeScript to get a ref with the Vue Composition API

The issue I'm facing is related to Vetur underlining 'null' in the following line: const firstRef = ref<HTMLElement>(null) <template> <input id="first" ref="firstRef"> <button type="button&q ...

Retrieve an array of existing fields in MongoDB by comparing a collection with an array of objects

I'm a newbie when it comes to Mongo and I'm attempting to compare an array with a collection of documents, returning a list of matching records. Let me break it down:First Array I have a collection (user) with the following documents: > db. ...

I need to compile a comprehensive inventory of all the publicly accessible attributes belonging to a Class/Interface

When working with TypeScript, one of the advantages is defining classes and their public properties. Is there a method to list all the public properties associated with a particular class? class Car { model: string; } let car:Car = new Car(); Object. ...