Home > Enterprise >  Comparing values from two arrays and creating objects accordingly
Comparing values from two arrays and creating objects accordingly

Time:12-02

First time writing a question, so I will try to be clear on what the issue is.

I have 2 arrays filled with different values and I am trying to map the values from first array which are inputs of value, to the second which are outputs.

If first object in the first array is smaller than the amount in second array it would be like the first object is sending all its value to the second object, and so on with each object until the value from the second object is fulfilled.

In this way i would like to map values from one object to the other, also if for example if value in the first object if bigger than the second object, he would be sending partial amount of first objects value to fulfill the whole second object and then partially also next one.... This is the arrays and example of how it should end.

I am using also BigNumber.js and formatUnit function to have numbers neat.

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"}
    ]

What I would like to do is the following :

I would like to nicely map so that in the end array looks in the following way :

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}]

What I have come up with is :

    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)

(response at the moment)

[
  { 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' }
]

vs what I would like to get:

[
  { 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 help would be much appreciated!

CodePudding user response:

I'm interpreting the problem as... given these types:

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

implement the following function:

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

whereby the senders in the senderArray send values to the recipients in the recipientArray, in order, but you keep the current sender until she sends all of here value, and you keep the current recipient until he receives all of her value.


Here's one way to do it:

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

  // copy 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; // perform transaction
    recipient.value -= value; // perform transaction
    transactionArray.push({ // record 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 is an imperative algorithm, where we copy the senderArray and recipientArray data and mutate the copies (as opposed to a pure functional algorithm without state changes).

The idea is that we take the first sender and the first recipient (if there is no first sender, or if there is no first recipient, then we're done) and determine the value to be sent. This is the minimum of the value the sender wants to send and the value the recipient wants to receive. Then we perform the transaction by subtracting this value from both the sender and the receiver. And we record the transaction in the output array. Finally, the sender is removed from the array if she has sent all her value, and the recipient is removed from the array if he has received all his value.

(Note that I compare the amount of value left to epsilon; I'm using JavaScript's standard floating-point arithmetic for calculations, so sometimes you might get a sender or a recipient with a tiny positive value remaining due to precision issues... after all, 0.1 0.2 - 0.3 !== 0. So I pick some epsilon I expect to be smaller than any actual amount we care about. If you use a different numerical data structure then you might change how this comparison is done.)

At the end we return the array of transactions, after possibly logging a warning if either a sender is left with some unsent value or a recipient is left with some unreceived value... which will happen if the sum of the sender values does not match the sum of the recipient values.


Let's test it out with:

const senderArray: Sender[] = [
  { 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" },
  { value: 0.1, sender: "eight" }
];

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

where I've added another sender to make the sums equal:

const transactionArray = getTransactionArray(senderArray, recipientArray);

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

/* [
{"sender":"one","recipient":"a","value":"0.0010"},
{"sender":"two","recipient":"a","value":"0.0100"},
{"sender":"three","recipient":"a","value":"0.1000"},
{"sender":"four","recipient":"a","value":"0.3890"},
{"sender":"four","recipient":"b","value":"2.6110"},
{"sender":"five","recipient":"b","value":"0.0020"},
{"sender":"six","recipient":"b","value":"0.0003"},
{"sender":"seven","recipient":"b","value":"0.8867"},
{"sender":"seven","recipient":"c","value":"4.1133"},
{"sender":"eight","recipient":"c","value":"0.1000"}
]*/

Looks good!

Playground link to code

CodePudding user response:

You could try something like this

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"}
    ]

const thirdArray = firstArray.map(firstElement => {
  // Calculate the total value of all recipients in secondArray
  const totalValue = secondArray.reduce((acc, secondElement) => acc   secondElement.value, 0);

  // Calculate the remaining value for each recipient in secondArray
  const remainingValue = secondArray.map(secondElement => {
    return { recipient: secondElement.recipient, value: secondElement.value / totalValue * firstElement.value };
  });

  // Create an array of objects with the desired properties
  return remainingValue.map(remainingElement => {
    return { sender: firstElement.sender, receiver: remainingElement.recipient, amount: remainingElement.value };
  });
});



console.log(thirdArray.flat())

  • Related