Generating objects based on comparing values between two arrays

First time tackling a question, so I will strive to articulate the problem clearly.

I have two arrays containing different values and my goal is to map the values from the first array (which are inputs) to the second array (which are outputs).

If the value of the first object in the first array is smaller than the amount in the second array, it's as if the first object is transferring all its value to the second object. This process continues until the value from the second object is completely fulfilled.

This mapping should take place between objects, where even if the value in the first object is larger than the second object, a partial amount from the first object’s value is sent to fulfill the entirety of the second object, and then partially to the next one... Here are the arrays and an example of how it should end.

To achieve this, I am utilizing BigNumber.js and the formatUnit function for tidy numbers.


        const firstArray = [ 
          { value: 0.001, sender: "one"},
          { value: 0.01, sender: "two"},
          { value: 0.1, sender: "three"},
          { value: 3.0, sender: "four"},
          { value: 0.002, sender: "five"},
          { value: 0.0003, sender: "six"},
          { value: 5.0, sender: "seven"}
        ]
    

        const secondArray = [ 
          { value: 0.5, recipient: "a"},
          { value: 3.5, recipient: "b"},
          { value: 4.2133, recipient: "c"}
        ]
    

The desired output would look like this:


        const thirdArray = [
            {sender : one, receiver : a, amount : 0.001},
            {sender : two, receiver : a, amount : 0.01},
            {sender : three, receiver : a, amount : 0.1},
            {sender : four, receiver : a, amount : 0.389},
            {sender : four, receiver : b, amount : 2.611},
            {sender : five, receiver : b, amount : 0.002},
            {sender : six, receiver : b, amount : 0.0003},
            {sender : seven, receiver : b, amount : 0.8867},
            {sender : seven, receiver : c, amount : 4.2133}
        ]
    

Here is what I have come up with:


        let i = 0;
        let j = 0;

        let thirdArray = [];

        while (i < firstArray.length) {
            let input = new BigNumber(firstArray[i].value);

            while (j < secondArray.length) {
                input = input.minus(new BigNumber(secondArray[j].value));
            
                // Format units for amount
                const formattedAmount = formatUnits(secondArray[j].value, -8);
                
                // Initialize responseObject
                const responseObj = {
                    sender: firstArray[i].sender,
                    receiver: secondArray[j].recipient,
                    amount: formattedAmount,
                };

                if (input.isLessThan(0)) {
                    let output = new BigNumber(secondArray[j].value);
                    output = output.minus(input.times(-1));
                    thirdArray.push({
                        ...responseObj,
                        amount: formatUnits(output.toNumber(), -8),
                    });
                    output = input.times(-1);
                    break;
                }
                thirdArray.push(responseObj);
                j += 1;
                if (input.isEqualTo(0)) break;
            }
            i += 1;
        }

        console.log(thirdArray)
    

(current output)


        [
            { sender: 'one', receiver: 'a', amount: '0.001' },
            { sender: 'two', receiver: 'a', amount: '0.01' },
            { sender: 'three', receiver: 'a', amount: '0.1' },
            { sender: 'four', receiver: 'a', amount: '0.5' },
            { sender: 'four', receiver: 'b', amount: '2.5' },
            { sender: 'five', receiver: 'b', amount: '0.002' },
            { sender: 'six', receiver: 'b', amount: '0.0003' },
            { sender: 'seven', receiver: 'b', amount: '3.5' },
            { sender: 'seven', receiver: 'c', amount: '1.5' }
        ]
    

Desired output:


        [
            { sender : one, receiver : a, amount : 0.001 },
            { sender : two, receiver : a, amount : 0.01 },
            { sender : three, receiver : a, amount : 0.1 },
            { sender : four, receiver : a, amount : 0.389 },
            { sender : four, receiver : b, amount : 2.611 },
            { sender : five, receiver : b, amount : 0.002 },
            { sender : six, receiver : b, amount : 0.0003 },
            { sender : seven, receiver : b, amount : 0.8867 },
            { sender : seven, receiver : c, amount : 4.2133 }
        ]
    

Any assistance on this matter would be highly appreciated!

Answer №1

After analyzing the issue, I understand it as follows:

interface Sender {
  sender: string;
  value: number;
}   
interface Recipient {
  recipient: string;
  value: number;
}
interface Transaction extends Sender, Recipient { }

The task is to create a function that does the following:

declare function getTransactionArray(
  senderArray: Sender[], 
  recipientArray: Recipient[]
): Transaction[];

This function should take an array of senders and an array of recipients, and then generate a transaction array where each sender sends values to corresponding recipients in order. The current sender remains until all their value is sent, while the current recipient remains until they receive all their value.


One possible implementation of this function is outlined here:

function getTransactionArray(senderArray: Sender[], recipientArray: Recipient[]) {    
  const epsilon = 1e-10; // considered zero threshold

  // making copies of arrays
  senderArray = senderArray.map(s => ({ ...s }));
  recipientArray = recipientArray.map(s => ({ ...s }));

  const transactionArray: Transaction[] = [];
  while (senderArray.length && recipientArray.length) {
    const sender = senderArray[0];
    const recipient = recipientArray[0];
    const value = Math.min(sender.value, recipient.value);
    sender.value -= value; // adjusting sender's value after transaction
    recipient.value -= value; // adjusting recipient's value after transaction
    transactionArray.push({ // recording the transaction
      sender: sender.sender, 
      recipient: recipient.recipient, 
      value 
    });
    if (!(sender.value > epsilon)) senderArray.shift();
    if (!(recipient.value > epsilon)) recipientArray.shift();
  }
  
  if (senderArray.length) console.log("WARNING! No receipients for", senderArray);
  if (recipientArray.length) console.log("WARNING! No senders for ", recipientArray);
  return transactionArray;
}

This solution uses an imperative approach by mutating copied arrays instead of functional programming without state changes.

The algorithm involves selecting the first sender and recipient, determining the transaction value based on what can be sent/received, and updating the values accordingly until completed. Transactions are recorded in the output array, and participants are removed once done with their values.

A tiny value comparison using epsilon is used due to JavaScript's floating-point arithmetic imprecisions.

Finally, any warnings are logged if a sender has unsent value or a recipient has unreceived value at the end.


Testing the function with sample data:

const senderArray: Sender[] = [
  { value: 0.001, sender: "one" },
  { value: 0.01, sender: "two" },
  { value: 0.1, sender: "three" },
  ...
];

const recipientArray: Recipient[] = [
  { value: 0.5, recipient: "a" },
  { value: 3.5, recipient: "b" },
  ...
];

Additional sender added to balance sums:

const transactionArray = getTransactionArray(senderArray, recipientArray);

console.log("[\n" + transactionArray.map(v =>
  JSON.stringify(({ ...v, value: v.value.toFixed(4) }))
).join(",\n") + "\n]");

Successful test results!

Link to code playground for testing

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

I encountered an issue with my autocomplete feature in Angular TypeScript where it was returning a [object Object] value

Issue with autocomplete displaying [object Object] value in Angular TypeScript I'm having trouble pinpointing the exact problem HTML snippet <mat-form-field style="margin-right: 10px;"> <input #productName matInput placeholder="Product" ...

It is impossible for Javascript to access an element that has been loaded using

After loading a div with PHP, I am attempting to access it from HTML using Javascript. However, when trying to get the element by its id, it keeps alerting as undefined. <html> <head> <script src="https://ajax.googleapis.com/ajax/libs/j ...

Troubleshooting: Why is the Array in Object not populated with values when passed during Angular App instantiation?

While working on my Angular application, I encountered an issue with deserializing data from an Observable into a custom object array. Despite successfully mapping most fields, one particular field named "listOffices" always appears as an empty array ([]). ...

Monitor every aspect of an object in AngularJS

Within my $scope, I have an object with multiple attributes, like so: $scope.content = { name : 'myname', description : 'mydescription', keyword : 'some keyword' } I am attempting to monitor any changes in eac ...

How to retrieve values from checkboxes generated dynamically in php using jquery

This is a unique question and not related to event binding or any suggested duplicates. Hello, I am facing an issue while trying to fetch the value of a checkbox to send it in an ajax request to a PHP page. The checkboxes are dynamically created using PHP ...

``What is the best way to include a JavaScript jQuery variable value within a PHP array

Let's consider the following scenario: var folderName = jQuery.trim($('#dirname').val()); var parentFolder = jQuery.trim($('#parent').val()); var dynamicUrl = '<?=$this->url(array('controller'=>'index ...

Encountering an issue: Unable to initiate a local server when running `npm start`

Currently diving into the world of React, I successfully set up a React app. However, upon running npm install after typing cd davidsapp, I encountered numerous warnings and errors. Subsequently, when issuing the command npm start, all of the errors are di ...

"The issue with Node/Express BodyParser is that it is not correctly retrieving the data from input fields, resulting in either empty values

I am facing an issue with my basic express set up. Despite following tutorials, I am unable to capture the data entered by a user in two inputs; a text input and a dropdown input. Here is how my app.js file is structured: var express = require('expr ...

Tips for appending the id of an active element to a URL

My goal was to include the id value of the active element in a URL and then redirect to that URL with a button click. HTML <div class="icon" tabindex="0" id="company"> <img src="company.png"> </div> <button type="submit" onclick= ...

What location is the optimal choice for documenting logs at a debugging level?

My team and I have been deeply contemplating the best location for writing a debug-level log during our development process. We are utilizing winston in conjunction with winston-daily-rotate-file to separate out different aspects of logging, as well as ne ...

Ways to maintain the integrity of external values?

As a newcomer to Python, I may have some naive questions. I am attempting to create a program that generates a two-dimensional array. One function populates a list and returns an array, while a second function takes the results of the first function and or ...

Classes in Typescript can implement interfaces to streamline the functionality

Recently, I've been working on creating my custom class called AxiosError and I can't help but feel like the code is a bit repetitive. //types.ts export interface IAxiosRequest{} export interface IAxiosResponse{} export interface IAxios ...

Adjusting the Size of Slideshow Pictures in a JavaScript Array to Match the Viewport

Welcome to my first post! Please excuse any formatting issues. I am currently working on a project where I am trying to create a slideshow using Javascript. I have successfully added images into an array and have the slideshow up and running. However, I ...

The reason why JavaScript doesn't treat "1-1" as 0 in an operation like it does with "123" to 123 during calculations

When dealing with JavaScript, the difference between "123" and "1-2" can be confusing. While "123" is recognized as a string type with a numerical value, "1-2" is also a string type but it does not allow for multiplication. Why doesn't JavaScript hand ...

Animate the movement of an ImageIcon across two jButtons within an array

I need help figuring out how to animate the movement of the image icon associated with ballpos as it moves between two JButtons within an array of JButton. The goal is for the image to switch buttons from 99 to 108 and back again each time a separate butto ...

Importing events from the calendar causes disarray in other data columns when sorted by date

I have a unique code that successfully imports my shared Google Calendar into a spreadsheet. In my medical office, I manage all appointments through a master Calendar. The calendar data includes start time, location, description, and title in columns B, ...

Extracting the "defined" type from a TypeScript property during runtime

My current task Presently, I am iterating through the keys of an object and transferring their values to another object. interface From { [key: string]: string; } let from: From = { prop1: "foo", prop2: "23", }; interface To { [key: str ...

Looking for an iframe that can adapt to varying content sizes and scale seamlessly with different screen dimensions?

I am new to advanced coding and currently working on my first responsive Wordpress site. I have a "Product Search" database/site that I'm trying to integrate into my website using an iFrame. I want the integration to look seamless without scroll bars ...

Loop through the component name and route path in ReactJs to efficiently organize and structure your application

In my route file coding for ASP.NET, I am creating routes by fetching details from the backend. Successfully getting details like [Contacts, Pipelines, Stages]. import * as React from 'react'; import { BrowserRouter, Redirect, Route } from &apos ...

Restore Bootstrap Dropdown values to their initial settings when clicked

I need a button that can reset all filter dropdown values to their default values. The current code I have only changes all values to "Filter" when reset, but I specifically need it to reset to "Car brand" and "Model". Here's my code: // set.... $(" ...