Expense Sharing Calculation The following is my solution to the problem presented on this CodeReview post:

Friends A, B, C, D go for a trip. They spend on various expenses. Cost of the expense is shared.
Example :

A spends 100 for breakfast for A, B, C and D
D spends 500 for cab for B and C
B spends 300 for lunch for A, B and C

Write a program to calculate how much each should get or each should give to one another. App should be scalable that number of friends can change


Design:
We need two data sets, which each respectively:

record transactions
tally amount due to each person

Simply strip off the info we need from the transactions and record either how much each person is owed, or how much each person should give.
Any input is welcome!
using System;
using System.Collections.Generic;

namespace CodeReview
{
    class Transaction
    {
        class Account
        {
            private int totalDebt = 0;
            private Dictionary&lt;char, int&gt; debtors = new Dictionary&lt;char, int&gt;();

            public Account(char initialDebtor, int initialDebt)
            {
                debtors.Add(initialDebtor, initialDebt);
            }

            public Dictionary&lt;char, int&gt; Debtors
            {
                get
                {
                    return debtors;
                }
            }

            public int TotalDebt
            {
                get
                {
                    foreach (char debtor in debtors.Keys)
                    {
                        totalDebt += debtors[debtor];
                    }

                    return totalDebt;
                }
            }
        }

        static void Main(string[] args)
        {
            Queue&lt;Tuple&lt;char, int, List&lt;char&gt;&gt;&gt; transactions = new Queue&lt;Tuple&lt;char, int, List&lt;char&gt;&gt;&gt;();
            Dictionary&lt;char, Account&gt; ledger = new Dictionary&lt;char, Account&gt;();

            transactions.Enqueue(Tuple.Create('A', 100, new List&lt;char&gt;() { 'A', 'B', 'C', 'D' }));
            transactions.Enqueue(Tuple.Create('D', 500, new List&lt;char&gt;() { 'B', 'C' }));
            transactions.Enqueue(Tuple.Create('B', 300, new List&lt;char&gt;() { 'A', 'B', 'C' }));

            while (transactions.Count &gt; 0)
            {
                Tuple&lt;char, int, List&lt;char&gt;&gt; transaction = transactions.Dequeue();

                foreach (char beneficiary in transaction.Item3)
                {
                    if (!beneficiary.Equals(transaction.Item1))
                    {
                        if (ledger.ContainsKey(beneficiary))
                        {
                            Dictionary&lt;char, int&gt; debtors = ledger[beneficiary].Debtors;

                            if (debtors.ContainsKey(transaction.Item1))
                            {
                                debtors[transaction.Item1] += transaction.Item2;
                            }
                            else
                            {
                                debtors.Add(transaction.Item1, transaction.Item2);
                            }
                        }
                        else
                        {
                            ledger.Add(beneficiary, new Account(transaction.Item1, transaction.Item2));
                        }
                    }
                }
            }

            foreach (char beneficiary in ledger.Keys)
            {
                Account account = ledger[beneficiary];

                Console.WriteLine(&quot;User &quot; + beneficiary + &quot; owes $&quot; + account.TotalDebt + &quot;:&quot;);

                foreach (char debtor in account.Debtors.Keys)
                {
                    int debt = account.Debtors[debtor];

                    Console.WriteLine(&quot; - $&quot; + debt + &quot; to &quot; + debtor);
                }
            }

            Console.ReadKey();
        }
    }
}
The main issue I can see, is the excessive use of generic data types like tuples and dictionaries instead of domain classes. That may be a matter of taste, but I prefer meaningful named domain classes like your Account class. 
In the Account class, the dictionary is exposed as public member. IMHO internal data structures should be encapsulated. The class should be extended by appropriated methods to manipulate that data structures. That would automatically move code like



if (debtors.ContainsKey(transaction.Item1))
{
    debtors[transaction.Item1] += transaction.Item2;
}
else
{
    debtors.Add(transaction.Item1, transaction.Item2);
}



into the domain class. Imagine use have to add items somewhere else, that kind of code must be duplicated each time.


Instead of


if (!beneficiary.Equals(transaction.Item1))
{
    ...
}




You could use

 if (beneficiary.Equals(transaction.Item1))
 {
     continue;
 }

 ....


to reduce nesting.


The instance variable debtors should be read-only.
Another small point: I would use strings to store the users because you can store names and you do not run into problems if the group becomes greater than 26 persons :P - of course, that is not relevant for the algorithm.




Generall Approach

Your approach is, to build up two dictionaries (the ledger and the debtors within the account class). The data structure of nested dictionaries matches the requirements exactly. If the requirements change, you have to restructure your whole code.

It is better to transform the data in a simple generally usable data structure and implement your algorithm for that data structure.

Such a simple data structure may be a list of Transactions:

public class Transaction
{
    public Transaction(string payer, string payee, int amount)
    {
        Payer = payer;
        Amount = amount;
        Payee = payee;
    }

    public string Payer { get; }
    public int Amount { get; }
    public string Payee { get; }
}


The algorithms could be implemented in a class like Bank:

public class Bank
{
    private readonly List&lt;Transaction&gt; myTransactions = new List&lt;Transaction&gt;();
    private readonly HashSet&lt;string&gt; myPayers = new HashSet&lt;string&gt;();
    private readonly HashSet&lt;string&gt; myPayees = new HashSet&lt;string&gt;();

    public void AddTransaction(string payer, string payee, int amount) 
    {
        myTransactions.Add(new Transaction(payer, payee, amount));
        myPayers.Add(payer);
        myPayees.Add(payee);
    }

    public void PrintPayees()
    {
        foreach (var payee in myPayees)
        {
            var trans = myTransactions.Where(t =&gt; t.Payee == payee);
            Console.WriteLine($"User {payee} owes ${trans.Sum(t =&gt; t.Amount)} total:");
            foreach (var groupedTransaction in trans.GroupBy(t =&gt; t.Payer))
                Console.WriteLine($"- ${groupedTransaction.Sum(t =&gt; t.Amount)} from {groupedTransaction.Key}");
        }
    }

    public void PrintPayer()
    {
        foreach (var payer in myPayers)
        {
            var trans = myTransactions.Where(t =&gt; t.Payer == payer);
            Console.WriteLine($"User {payer} gets ${trans.Sum(t =&gt; t.Amount)} total:");
            foreach (var groupedTransaction in trans.GroupBy(t =&gt; t.Payee))
                Console.WriteLine($"- ${groupedTransaction.Sum(t =&gt; t.Amount)} to {groupedTransaction.Key}");
        }
    }
}


Usage base on the list of tuples:

void Main()
{
    var bank = new Bank();

    foreach (var transaction in new[] { Tuple.Create("A", 100, new [] { "A", "B", "C", "D" }),
                                        Tuple.Create("D", 500, new [] { "B", "C" }),
                                        Tuple.Create("B", 300, new [] { "A", "B", "C" }) })
    {
        var payer = transaction.Item1;
        var amount = transaction.Item2;
        foreach (var payee in transaction.Item3)
            if (payee != payer)
                bank.AddTransaction(payer, payee, amount);
    }   

    bank.PrintPayees();
    bank.PrintPayer();
}