Home > Software design >  C# list - finding the occurrence of elements and adding a position counter to the list
C# list - finding the occurrence of elements and adding a position counter to the list

Time:05-27

I need some help on this puzzle with list manipulation. I'm not sure how to go about solving this.

I have a list of data and I need to add an occurrence counter id to each row where the date is grouped and the amount are matched.

e.g on same day if the amounts are the same then increase the counter id for each time they are the same

Original list of data

text    | date       | amount
memo 01 | 2022-05-25 | 10
memo 02 | 2022-05-25 | 20
memo 03 | 2022-05-25 | 20
memo 04 | 2022-05-25 | 30
memo 05 | 2022-05-25 | 15
memo 06 | 2022-05-25 | 20
memo 07 | 2022-05-25 | 10
memo 08 | 2022-05-25 | 40
memo 09 | 2022-05-26 | 20
memo 10 | 2022-05-26 | 15
memo 11 | 2022-05-26 | 30
memo 12 | 2022-05-26 | 20

Output desired (with occurrence counter added)

text    | date       | amount | occur
memo 01 | 2022-05-25 |   10   | 1
memo 02 | 2022-05-25 |   20   | 1
memo 03 | 2022-05-25 |   20   | 2
memo 04 | 2022-05-25 |   30   | 1
memo 05 | 2022-05-25 |   15   | 1
memo 06 | 2022-05-25 |   20   | 3
memo 07 | 2022-05-25 |   10   | 2
memo 08 | 2022-05-25 |   40   | 1
memo 09 | 2022-05-26 |   20   | 1
memo 10 | 2022-05-26 |   15   | 1
memo 11 | 2022-05-26 |   30   | 1
memo 12 | 2022-05-26 |   20   | 2

Here's my code to create my test list of data

var myList = new List<(string, DateTime, decimal)> 
     {
         ("memo 01",new DateTime(2022, 05, 25),10),
         ("memo 02",new DateTime(2022, 05, 25),20),
         ("memo 03",new DateTime(2022, 05, 25),20),
         ("memo 04",new DateTime(2022, 05, 25),30),
         ("memo 05",new DateTime(2022, 05, 25),15),
         ("memo 06",new DateTime(2022, 05, 25),20),
         ("memo 07",new DateTime(2022, 05, 25),10),
         ("memo 08",new DateTime(2022, 05, 25),40),
         ("memo 09",new DateTime(2022, 05, 26),20),
         ("memo 10",new DateTime(2022, 05, 26),15),
         ("memo 11",new DateTime(2022, 05, 26),30),
         ("memo 12",new DateTime(2022, 05, 26),20)
       };

myList.ForEach(x => Console.WriteLine($"{x.Item1} | {x.Item2.ToString("yyyy-MM-dd")} | {x.Item3}"));

CodePudding user response:

Using Dictionaries but expanded for better understanding.

    var myList = new List<(string text, DateTime date, decimal occurence)>
 {
     ("memo 01",new DateTime(2022, 05, 25),10),
     ("memo 02",new DateTime(2022, 05, 25),20),
     ("memo 03",new DateTime(2022, 05, 25),20),
     ("memo 04",new DateTime(2022, 05, 25),30),
     ("memo 05",new DateTime(2022, 05, 25),15),
     ("memo 06",new DateTime(2022, 05, 25),20),
     ("memo 07",new DateTime(2022, 05, 25),10),
     ("memo 08",new DateTime(2022, 05, 25),40),
     ("memo 09",new DateTime(2022, 05, 26),20),
     ("memo 10",new DateTime(2022, 05, 26),15),
     ("memo 11",new DateTime(2022, 05, 26),30),
     ("memo 12",new DateTime(2022, 05, 26),20)
   };
        //final output list
        var output = new List<(string text, DateTime date, decimal occurence, int count)>();

        //dictionary to store the counts
        var dictionary = new Dictionary<string, int>();
        foreach (var item in myList)
        {
            var key = $"{item.date}_{item.occurence}";
            if (dictionary.TryGetValue(key, out int count))
            {
                //populate the output list
                output.Add((item.text, item.date, item.occurence, count   1));

                //update dictionary
                dictionary[key]  = 1;
            }
            else
            {
                //populate the output list
                output.Add((item.text, item.date, item.occurence, 1));

                //update dictionary
                dictionary[key] = 1;
            }
        }

        output.ForEach(x => Console.WriteLine($"{x.text} | {x.date.ToString("yyyy-MM-dd")} | {x.occurence} | {x.count}"));

CodePudding user response:

You can use Dictionary to count occurrencies:

  ...

  Dictionary<(DateTime, decimal), int> occs = new();

  foreach (var x in myList) {
    if (!occs.TryAdd((x.Item2, x.Item3), 1))
      occs[(x.Item2, x.Item3)]  = 1;

    Console.WriteLine($"{x.Item1} | {x.Item2.ToString("yyyy-MM-dd")} | {x.Item3} | {occs[(x.Item2, x.Item3)]}");
  }

Outcome:

memo 01 | 2022-05-25 | 10 | 1
memo 02 | 2022-05-25 | 20 | 1
memo 03 | 2022-05-25 | 20 | 2
memo 04 | 2022-05-25 | 30 | 1
memo 05 | 2022-05-25 | 15 | 1
memo 06 | 2022-05-25 | 20 | 3
memo 07 | 2022-05-25 | 10 | 2
memo 08 | 2022-05-25 | 40 | 1
memo 09 | 2022-05-26 | 20 | 1
memo 10 | 2022-05-26 | 15 | 1
memo 11 | 2022-05-26 | 30 | 1
memo 12 | 2022-05-26 | 20 | 2

CodePudding user response:

There are many ways to achieve the outcome you want:

var tempList = new List<(string, DateTime, decimal, int)>();
myList.ForEach(x => 
{
   x.Item4 = tempList.Where(y => y.Item2.Date == x.Item2.Date && y.Item3 == x.Item3 ).Count()   1;
   tempList.Add(x);

   Console.WriteLine($"{x.Item1} | {x.Item2.ToString("yyyy-MM-dd")} | {x.Item3} | {x.Item4}");
});

CodePudding user response:

Here's my LINQ only way:

var output = 
    list
        .Select((m, n) => new { memo = m, n })
        .GroupBy(
            x => new { x.memo.date, x.memo.amount },
            x => new { order = x.n, x.memo.text })
        .SelectMany(gxs => gxs.Select((gx, n) => new
        {
            gx.order,
            result = new
            {
                gx.text,
                gxs.Key.date,
                gxs.Key.amount,
                occur = n   1,
            }
        }))
        .OrderBy(x => x.order)
        .Select(x => x.result)
        .ToList();

Start with this data:

var list = new List<(string text, DateTime date, decimal amount)>
{
    ("memo 01",new DateTime(2022, 05, 25),10),
    ("memo 02",new DateTime(2022, 05, 25),20),
    ("memo 03",new DateTime(2022, 05, 25),20),
    ("memo 04",new DateTime(2022, 05, 25),30),
    ("memo 05",new DateTime(2022, 05, 25),15),
    ("memo 06",new DateTime(2022, 05, 25),20),
    ("memo 07",new DateTime(2022, 05, 25),10),
    ("memo 08",new DateTime(2022, 05, 25),40),
    ("memo 09",new DateTime(2022, 05, 26),20),
    ("memo 10",new DateTime(2022, 05, 26),15),
    ("memo 11",new DateTime(2022, 05, 26),30),
    ("memo 12",new DateTime(2022, 05, 26),20)
};

I get this result:

output

CodePudding user response:

Here is a little object-oriented way.

public class Transactions : IEnumerable<KeyValuePair<Transaction, int>>
{
    private readonly Dictionary<Transaction, int> _values = new();
    public void Add(Transaction transaction)
    {
        if (_values.ContainsKey(transaction))
        {
            _values[transaction]  = 1;
        }
        else
        {
            _values.Add(transaction, 1);
        }
    }
    public IEnumerator<KeyValuePair<Transaction, int>> GetEnumerator()
    {
        return _values.GetEnumerator();
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}
public record Transaction(DateTime Date, decimal Amount);
//Program.cs
var myList = new List<Transaction>
{
    new(new DateTime(2022, 05, 25), 10),
    new(new DateTime(2022, 05, 25), 20),
    new(new DateTime(2022, 05, 25), 20),
    new(new DateTime(2022, 05, 25), 30),
    new(new DateTime(2022, 05, 25), 15),
    new(new DateTime(2022, 05, 25), 20),
    new(new DateTime(2022, 05, 25), 10),
    new(new DateTime(2022, 05, 25), 40),
    new(new DateTime(2022, 05, 26), 20),
    new(new DateTime(2022, 05, 26), 15),
    new(new DateTime(2022, 05, 26), 30),
    new(new DateTime(2022, 05, 26), 20)
};
var output = new Transactions();
myList.ForEach(item => output.Add(item));
foreach (var keyValuePair in output)
{
    Console.WriteLine(keyValuePair);
}

Explanation: We have a record here which gives automatic value comparision, and then we create a custom collection to handle the new inserts.

  •  Tags:  
  • c#
  • Related