iteration above below in Dictionary

aekzof

Member
Joined
Sep 6, 2023
Messages
5
Programming Experience
Beginner
how i do this:
        double maxVolume = double.MinValue; // Initialize with the smallest possible value
            double Price = 0;
            double Volume = 0;
            
            double correspondingKey = 0.0;

            double AllVolumes = 0.0;
            double totalVolumes = 0.0;


            Dictionary<double, double> MyVolumeDictionary = new Dictionary<double, double>();


            // Add data to the dictionary
            MyVolumeDictionary.Add(9638.0, 3217.0);
            MyVolumeDictionary.Add(9637.0, 3215.0);
            MyVolumeDictionary.Add(9636.0, 3215.0);
            MyVolumeDictionary.Add(9635.0, 3215.0);
            MyVolumeDictionary.Add(9634.0, 3215.0);
            MyVolumeDictionary.Add(9633.0, 3215.0);
            MyVolumeDictionary.Add(9632.0, 3215.0);
            MyVolumeDictionary.Add(9631.0, 3215.0);
            MyVolumeDictionary.Add(9630.0, 3215.0);
            MyVolumeDictionary.Add(9629.0, 3215.0);
            MyVolumeDictionary.Add(9628.0, 3215.0);
            MyVolumeDictionary.Add(9627.0, 3215.0);
            MyVolumeDictionary.Add(9626.0, 3215.0);
            MyVolumeDictionary.Add(9625.0, 3215.0);
            MyVolumeDictionary.Add(9624.0, 3215.0);
            MyVolumeDictionary.Add(9623.0, 4311.0);
            MyVolumeDictionary.Add(9622.0, 5071.0);
            MyVolumeDictionary.Add(9621.0, 6056.0);
            MyVolumeDictionary.Add(9620.0, 9087.0);
            MyVolumeDictionary.Add(9619.0, 10819.0);
            MyVolumeDictionary.Add(9618.0, 14921.0);
            MyVolumeDictionary.Add(9617.0, 16496.0);
            MyVolumeDictionary.Add(9616.0, 17601.0);
            MyVolumeDictionary.Add(9615.0, 19626.0);
            MyVolumeDictionary.Add(9614.0, 21800.0);
            MyVolumeDictionary.Add(9613.0, 21973.0);
            MyVolumeDictionary.Add(9612.0, 22168.0);
            MyVolumeDictionary.Add(9611.0, 18791.0);
            MyVolumeDictionary.Add(9610.0, 15700.0);
            MyVolumeDictionary.Add(9609.0, 16900.0);
            MyVolumeDictionary.Add(9608.0, 13971.0);
            MyVolumeDictionary.Add(9607.0, 12367.0);
            MyVolumeDictionary.Add(9606.0, 8706.0);
            MyVolumeDictionary.Add(9605.0, 7134.0);
            MyVolumeDictionary.Add(9604.0, 6107.0);
            MyVolumeDictionary.Add(9603.0, 5341.0);
            MyVolumeDictionary.Add(9602.0, 3827.0);

            // Access dictionary data
            foreach (var kvp in MyVolumeDictionary)
            {
                Price = kvp.Key;
                Volume = kvp.Value;
               
AllVolumes  += Volume;

                if (kvp.Value > maxVolume)
                {
                    maxVolume = kvp.Value;
                    correspondingKey = kvp.Key;
                }

            }
                 Print("correspondingKey :" + correspondingKey + " maxVolume : " + maxVolume);

            

            Print("AllVolumes:" +AllVolumes);

Hello
From this script, I am searching for the price with the highest volume.

Results:

correspondingKey = 9612.0,
maxVolume= 22168.0,
AllVolumes= 327000. //total of volumes in this sample

What I wish to do next, using correspondingKey = 9612.0 as a reference,
is to find, as a priority, the first pair directly above correspondingKey, which is as follows:

1. Above: 9614.0 - 9613.0

I will add the volumes of this pair, resulting in a total of 43773.

Then, I will explicitly take the pair that is below correspondingKey, which is as follows:
2. Below:9611.0 - 9610.0

I will add the volumes of this pair, resulting in a total of 34491.
I repeat this operation, alternating between pairs, one above and one below, without repeating the price.
At each pair iteration, I add the volumes until the total volume exceeds, for example, 240000.
This total of 240000 must include the volume of correspondingKey = 9612.0.

To clarify, up to now, we have dealt with only 2 pairs:
  1. Above: 9614.0 - 9613.0
  2. Below: 9611.0 - 9610.0
Continuing with:
3. Above: 9616.0 - 9615.0
4. Below: 9609.0 - 9608.0
5. Above: 9618.0 - 9617.0
6. Below: 9607.0 - 9606.0
7. Above: 9619.0 - 9618.0

As soon as the total volume surpasses this threshold, for example, 240000, we stop and retrieve the minimum and maximum prices used to calculate this volume.
In this case, the values are:
Minimum: 9606.0
Maximum: 9620.0

I have spent hours trying to achieve this, but I have not succeeded yet, and I won't be able to solve this problem on my own.
Please help me find a solution.
 
A list of tuples would likely be a better data structure. If you need to have the tuples in some kind of sorted order, then a heap or tree maybe optimal depending on the insertion frequency. If there are not going to be any insertions after loading, then simply sorting the list of tuples maybe enough.
 
From this script

As a quick aside. In C#, code is called "code", not "script" (except when you are writing C# code for Unity).
 
Voodoo:

C#:
            list.Add((9603.0, 5341.0));
            list.Add((9602.0, 3827.0));

           
            var maxVol = list.MaxBy(t => t.Volume);
       
            var sum = maxVol.Volume;
            var pairs =
                  list
                    .Where(t => t.Price > maxVol.Price)
                    .Reverse()
                    .Chunk(2)
                    .Zip(
                        list.Where(t => t.Price < maxVol.Price)
                    .Chunk(2)
                  )
                  .SelectMany(tz => new []{ tz.Item1, tz.Item2 })
                  .Select(c => new{ MinPrice = c.Min(t => t.Price), MaxPrice = c.Max(t => t.Price), RollingSum = (sum += c.First().Volume + c.Last().Volume) } )
                  .Where(at => at.RollingSum < 240000)
                  .ToArray();
           
            var minPr = pairs.Min(at => at.MinPrice);
            var maxPr = pairs.Max(at => at.MaxPrice);

 
Last edited:
In this case, the values are:
Minimum: 9606.0
Maximum: 9620.0

I disagree, but maybe I misunderstood.

For some reason you said 9618 twice:

5. Above: 9618.0 - 9617.0
6. Below: 9607.0 - 9606.0
7. Above: 9619.0 - 9618.0

I agree with the 9606, but not the 9620. By the time we do that, we are at 240921 which is not less than 240000

C#:
Dumping object(<>f__AnonymousType0`3[])
[
   {
   MaxPrice    : 9614
   MinPrice    : 9613
   RollingSum  : 65941
   ToString()  : { MinPrice = 9613, MaxPrice = 9614, RollingSum = 65941 }
   },
   {
   MaxPrice    : 9611
   MinPrice    : 9610
   RollingSum  : 100432
   ToString()  : { MinPrice = 9610, MaxPrice = 9611, RollingSum = 100432 }
   },
   {
   MaxPrice    : 9616
   MinPrice    : 9615
   RollingSum  : 137659
   ToString()  : { MinPrice = 9615, MaxPrice = 9616, RollingSum = 137659 }
   },
   {
   MaxPrice    : 9609
   MinPrice    : 9608
   RollingSum  : 168530
   ToString()  : { MinPrice = 9608, MaxPrice = 9609, RollingSum = 168530 }
   },
   {
   MaxPrice    : 9618
   MinPrice    : 9617
   RollingSum  : 199947
   ToString()  : { MinPrice = 9617, MaxPrice = 9618, RollingSum = 199947 }
   },
   {
   MaxPrice    : 9607
   MinPrice    : 9606
   RollingSum  : 221020
   ToString()  : { MinPrice = 9606, MaxPrice = 9607, RollingSum = 221020 }
   },
   {
   MaxPrice    : 9620
   MinPrice    : 9619
   RollingSum  : 240926
   ToString()  : { MinPrice = 9619, MaxPrice = 9620, RollingSum = 240926 }
   },
   {
   MaxPrice    : 9605
   MinPrice    : 9604
   RollingSum  : 254167
   ToString()  : { MinPrice = 9604, MaxPrice = 9605, RollingSum = 254167 }
   },
   {
   MaxPrice    : 9622
   MinPrice    : 9621
   RollingSum  : 265294
   ToString()  : { MinPrice = 9621, MaxPrice = 9622, RollingSum = 265294 }
   },
   {
   MaxPrice    : 9603
   MinPrice    : 9602
   RollingSum  : 274462
   ToString()  : { MinPrice = 9602, MaxPrice = 9603, RollingSum = 274462 }
   }
]

If your intended logic is to include the ones that cause a breach of 240000, take a look at C# Online Compiler | .NET Fiddle
 
Last edited:
If your intended logic is to include the ones that cause a breach of 240000, take a look at C# Online Compiler | .NET Fiddle

Once again, you are correct. I didn't want to go into detail earlier.
In this example, the total volume is 327,000.
I need to take 70% of the volume -> 70%: 228,900.
With this logic, there will be an excess volume to reach 70%, which will be found as 240,926, as you indicated.

240,926 is equal to 73.6%.

MinPrice: 9602
MaxPrice: 9620

Absolutely correct.




I apologize; there was a detail that escaped me and caused an error with this code.
In this logic, the maximum volume (maxVol) moves from one price to another, either upwards or downwards,

assuming that the max volume is above only two price .

For example:


maxvol:
    var list = new List<(double Price, double Volume)>();
       
            list.Add((9638.0, 3217.0));
            list.Add((9637.0, 3215.0));
            list.Add((9636.0, 3215.0));
            list.Add((9635.0, 3215.0));
            list.Add((9634.0, 3215.0));
            list.Add((9633.0, 3215.0));
            list.Add((9632.0, 3215.0));
            list.Add((9631.0, 3215.0));
            list.Add((9630.0, 3215.0));
            list.Add((9629.0, 3215.0));
            list.Add((9628.0, 3215.0));
            list.Add((9627.0, 3215.0));
            list.Add((9626.0, 3215.0));
            list.Add((9625.0, 3215.0));
            list.Add((9624.0, 3215.0));
            list.Add((9623.0, 4311.0));
            list.Add((9622.0, 5071.0));
            list.Add((9621.0, 6056.0));
            list.Add((9620.0, 9087.0));
            list.Add((9619.0, 10819.0));
            list.Add((9618.0, 14921.0));
            list.Add((9617.0, 16496.0));
            list.Add((9616.0, 17601.0));
            list.Add((9615.0, 19626.0));
            list.Add((9614.0, 21800.0));
            list.Add((9613.0, 21973.0));
            list.Add((9612.0, 22168.0));  // MAXVOL
            list.Add((9611.0, 18791.0));
            list.Add((9610.0, 15700.0));
           //  list.Add((9609.0, 16900.0));
           // list.Add((9608.0, 13971.0));
           // list.Add((9607.0, 12367.0));
           // list.Add((9606.0, 8706.0));
           // list.Add((9605.0, 7134.0));
           // list.Add((9604.0, 6107.0));
           // list.Add((9603.0, 5341.0));
           // list.Add((9602.0, 3827.0));





So, if we remove the prices from 9602 to 9609, the code returns an incorrect result:

MinPrice: 9610
MaxPrice: 9614

In this case, the total volume is 252,647.
Normally, if we need to reach 240,000, it should find:
MinPrice: 9610
MaxPrice: 9635
SumVolume: 243,000
 
Last edited:
I didn't really understand the revised version, but it's probably because Zip stops zipping if one of the sequences is too short. Probably easiest to artificially extend the se sequences being zipped
 
Last edited:
Last fish; next time you're going to have to pick up the rod


In term of how it works:

* find max volume in list
* find the price that goes with it
* find all elements with a greater price
* reverse the order of them (so price is low to high)
* concat on a load of zero volume items with the same max price
* chunk the list into pairs of items
* zip (interleave) it with the same thing but for items with prices less than the maxvol's price

We now have above pair/below pair/above pair/below pair

* run a rolling sum (naughty, to perform a linq query with side effects; could use aggregate for this but it's not really any better) on every item
* restrict to only items with a rolling sum less than the threshold
* find the min and max price in this restricted set
 
Last edited:
Hello
Since I'm a beginner, it was certain that I didn't know certain things.
This code only works under C# 7, unfortunately, it needs to be compiled in a project that exclusively supports only C# 5.0 and .NET 4.8.
Is there a solution to convert this code to C# 5.0?
 
The code above was meant to demonstrate a way to solve the problem. It read not meant to just be a a drop in solution because we try not to do your homework for you since we are not a code writing service.

Take time to understand the solution proposed, and then you can implement it with that facilities available to you in that language/framework version. I personally suspect that all you are missing are some of the newer LINQ extension methods, rather than running into a newer language feature.

Also, if you take time to understand the code, you can implement it without using LINQ. In my opinion, some bits may even be simplified because there are some concepts that are easier to implement procedurally, rather than functionally.
 
Here's my non-LINQ implementation. I made some assumptions about what's suppose to happen if pairs cannot be found.
C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;

public static class MyExtensions
{
    public static T MyMaxBy<T, TKey>(this IEnumerable<T> items, Func<T, TKey> getGet)
    {
        return items.First();
    }

    public static IEnumerable<T[]> MyChunk<T>(this IEnumerable<T> items, int size)
    {
        yield return items.Take(size).ToArray();
    }
}

public class Program
{
    enum PairsIndex : int { Max = 0, NotUsed = 1, Min = 2 };

    public static void Main()
    {
        const double CapVolumePercentage = 0.70;
        const int ChunkSize = 2;

        var list = new List<(double Price, double Volume)>
        {
            (9638.0, 3217.0),
            (9637.0, 3215.0),
            (9636.0, 3215.0),
            (9635.0, 3215.0),
            (9634.0, 3215.0),
            (9633.0, 3215.0),
            (9632.0, 3215.0),
            (9631.0, 3215.0),
            (9630.0, 3215.0),
            (9629.0, 3215.0),
            (9628.0, 3215.0),
            (9627.0, 3215.0),
            (9626.0, 3215.0),
            (9625.0, 3215.0),
            (9624.0, 3215.0),
            (9623.0, 4311.0),
            (9622.0, 5071.0),
            (9621.0, 6056.0),
            (9620.0, 9087.0),
            (9619.0, 10819.0),
            (9618.0, 14921.0),
            (9617.0, 16496.0),
            (9616.0, 17601.0),
            (9615.0, 19626.0),
            (9614.0, 21800.0),
            (9613.0, 21973.0),
            (9612.0, 22168.0),
            (9611.0, 18791.0),
            (9610.0, 15700.0),
            (9609.0, 16900.0),
            (9608.0, 13971.0),
            (9607.0, 12367.0),
            (9606.0, 8706.0),
            (9605.0, 7134.0),
            (9604.0, 6107.0),
            (9603.0, 5341.0),
            (9602.0, 3827.0),
        };

        list.Sort((a, b) => Math.Sign(b.Price - a.Price));

        int maxVolumeIndex = 0;
        var maxVolume = list[0].Volume;
        var totalVolume = list[0].Volume;
        for(int i  = 1; i < list.Count; i++)
        {
            var volume = list[i].Volume;
            totalVolume += volume;
            if (volume > maxVolume)
            {
                maxVolume = volume;
                maxVolumeIndex = i;
            }
        }
        Console.WriteLine($"Total volume: {totalVolume}");
        Console.WriteLine($"Maximum volume: {maxVolume}");

        var capVolume = totalVolume * CapVolumePercentage;
        Console.WriteLine($"Seeking {CapVolumePercentage:p} volume: {capVolume}");

        Console.WriteLine($"Price at maximum volume: {list[maxVolumeIndex].Price}");
        var sumVolume = maxVolume;
        int[] pairsIndex = new int[3] { maxVolumeIndex, 0, maxVolumeIndex };
        for (int misses = 0, delta = -1; sumVolume < capVolume && misses < 2; misses++, delta *= -1)
        {
            int listIndex = pairsIndex[delta + 1];
            if ((0 <= listIndex - ChunkSize) && (listIndex + ChunkSize < list.Count))
            {
                Console.Write("Price: ");
                for (int i = 0; i < ChunkSize; i++)
                {
                    listIndex += delta;
                    Console.Write($"{list[listIndex].Price} ");
                    sumVolume += list[listIndex].Volume;
                }
                pairsIndex[delta + 1] = listIndex;
                Console.WriteLine($"Sum = {sumVolume}");
                misses--;
            }
        }

        if (sumVolume < capVolume)
            Console.WriteLine($"Goal cap volume of {capVolume} could not be reached.");

        Console.WriteLine($"Found volume: {sumVolume}");
        Console.WriteLine($"Minimum Price: {list[pairsIndex[(int)PairsIndex.Min]].Price}");
        Console.WriteLine($"Maximum Price: {list[pairsIndex[(int)PairsIndex.Max]].Price}");
    }
}

The alternating between above and below is effected by the delta *= -1. When the value is negative, we are working towards the beginning of the list (e.g. "above" in your case since your list is ordered by price from high to low). When the value is positive we are working towards the end of the list (e.g. "below"). To keep track of the progress going on either direction, the pairsIndex array holds the indices of the maximum and minimum pairs extreme. Pairs index is actually a misnomer. It should be chunk index since the code can handle more than just pairs. The misses variable is my hack for preventing an infinite loop in case the goal 70% of total volume cannot be achieved.
 
Last edited:
Back
Top Bottom