Get first max value from collection of dictionaries with the help of linq

Ant6729

Well-known member
Joined
Jan 22, 2019
Messages
56
Programming Experience
Beginner
Hello!
I have output, like:
JSON:
    [
        {
            "serviceType": "MOVE",
            "directionCost": {
                "6ce50eb2-66cc-4581-a0fa-896454312ac7": 7000,
                "31a5d10d-db67-45d2-8efc-5b3f210a3bc0": 9265,
                "737212d9-cc3d-45bf-8c5e-43cad8f2f346": 8773
            },
            "id": "3c951dd2-c515-4189-9b78-0390b5c47d9d",
            "createdOn": "2021-04-05T20:14:29.817Z"
        },
        {
            "serviceType": "MOVE",
            "directionCost": {
                "6b1f74b4-7e0b-4aef-aa2c-eee89f2aa039": 2639,
                "1be99c1f-64ec-43da-a723-74abe60d4d15": 4824,
                "cff4791b-514a-4c71-b409-375e24cc0751": 3818
            },
            "id": "7c7b59ae-306b-455c-bcb0-3690cdbc6cb2",
            "createdOn": "2021-02-08T09:10:29.83Z"
        },
        {
            "serviceType": "MOVE",
            "directionCost": {
                "77074837-7ec3-4fbe-8c68-ff4f0f6c76a5": 2175,
                "c9c9259b-c010-4e1b-9303-be49eadcdfc9": 4404,
                "d8ccc562-6648-4683-b6b1-4061b3b4a2ef": 3271
            },
            "id": "e182fcbc-461d-447b-9239-1344e3f4f211",
            "createdOn": "2021-04-22T13:45:17.282Z"
        }
    ]
I try to write a query, that enables me to get output with the first minimal directionCost only like:
JSON:
    [
        {
            "serviceType": "MOVE",
            "directionCost": {
                "6ce50eb2-66cc-4581-a0fa-896454312ac7": 7000
            },
            "id": "3c951dd2-c515-4189-9b78-0390b5c47d9d",
            "createdOn": "2021-04-05T20:14:29.817Z"
        },
        {
            "serviceType": "MOVE",
            "directionCost": {
                "6b1f74b4-7e0b-4aef-aa2c-eee89f2aa039": 2639
            },
            "id": "7c7b59ae-306b-455c-bcb0-3690cdbc6cb2",
            "createdOn": "2021-02-08T09:10:29.83Z"
        },
        {
            "serviceType": "MOVE",
            "directionCost": {
                "77074837-7ec3-4fbe-8c68-ff4f0f6c76a5": 2175
            },
            "id": "e182fcbc-461d-447b-9239-1344e3f4f211",
            "createdOn": "2021-04-22T13:45:17.282Z"
        }
    ]
I try to write my query like:
C#:
            var finalResults = baseService.Where(
                .Select(x => x.DirectionCost.FirstOrDefault(el => el.Value == x.DirectionCost.Values.Min()));
but have output in a format like:
JSON:
    [
        {
            "key": "5f099185-b9fc-4ce9-b8bd-79d2ad6470d6",
            "value": 16443
        },
        {
            "key": "51ea3e7b-92b6-4cd9-a0f8-413f105c1ee0",
            "value": 9844
        },
        {
            "key": "6622409a-f767-4268-ae3a-69cb1cd6bd30",
            "value": 10695
        }
    ]
But I need other information also.
Could you help me?
 
Last edited by a moderator:
This code doesn't look like it would even compile:
C#:
            var finalResults = baseService.Where(
                .Select(x => x.DirectionCost.FirstOrDefault(el => el.Value == x.DirectionCost.Values.Min()));

Anyway, the way to do things would be to iterate over the original enumerable, and copy the values from that enumerable into another another enumerable, but change just the directionCost property. This could be more easily done with the C# 10.0 with keyword to minimize the boilerplate of copying one property to another property. Anyway, the code would look something like the following pseudo-code if you don't have access to with:

C#:
var final = original.Select(
    o => new {
        serviceType = o.serviceType,
        directionCost = o.directionCost
                         .OrderByDescending(d => d.Value)
                         .First(),
        id = o.id,
        createdOn = o.createdOn
    }
);
 
Thanks for the idea of using a new model to map the data.
But it is interesting to understand, how I could take min result, but not the first one, because it could be more than 3000 rows in each dictionary and I don't want to use order by here.
Actually, I try to adapt your decision like:

initial:
var final2 = modelList.Select(
                o => new
                {
                    serviceType = o.ServiceType,
                    directionCost = o.DirectionCost
                        //.Min(d => d.Value)
                        .Select(d => new Dictionary<Guid, int>())
                }
            );

But I still do not understand how to implement this.
 
See line 6 in post #2. It will return the first one.
 
Here is another sample I simplified for myself to understand my task better. Appreciate any teaching help.
Sample:
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using JsonSerializer = Newtonsoft.Json.JsonSerializer;

namespace DictionaryLinq
{
    public class Program
    {
      
        static void Main(string[] args)
        {
            var modelList = new List<BaseService>();

            var students1 = new Dictionary<Guid, int>()
            {
                {Guid.NewGuid(),1 },
                {Guid.NewGuid(),2 },
                {Guid.NewGuid(),3 }
            };

            var students2 = new Dictionary<Guid, int>()
            {
                {Guid.NewGuid(),4 },
                {Guid.NewGuid(),5 },
                {Guid.NewGuid(),6 }
            };

            var students3 = new Dictionary<Guid, int>()
            {
                {Guid.NewGuid(),7 },
                {Guid.NewGuid(),8 },
                {Guid.NewGuid(),9 }
            };

            var model1 = new BaseService();
            var model2 = new BaseService();
            var model3 = new BaseService();

            model1.DirectionCost = students1;
            model2.DirectionCost = students2;
            model3.DirectionCost = students3;

            model1.ServiceType = ServiceType.Move;
            model2.ServiceType = ServiceType.Luggage;
            model3.ServiceType = ServiceType.Other;

            modelList.Add(model1);
            modelList.Add(model2);
            modelList.Add(model3);

          
            var final1 = modelList.Select(
                o => new {
                    serviceType = o.ServiceType,directionCost = o.DirectionCost
                        .OrderBy(d => d.Value)
                        .First()
                }
            );

            foreach (var element in final1)
            {
                var jsonString = JsonConvert.SerializeObject(element);

                Console.WriteLine(jsonString);
            }

            Console.WriteLine();

            var final2 = modelList.Select(
                o => new
                {
                    serviceType = o.ServiceType,
                    directionCost = o.DirectionCost
                        //.Min(d => d.Value)
                        .Select(d => new Dictionary<Guid, int>())
                }
            );

            foreach (var element in final2)
            {
                var jsonString = JsonConvert.SerializeObject(element);

                Console.WriteLine(jsonString);
            }
        }
    }
  
    public class BaseService
    {
        public ServiceType ServiceType { get; set; }
        public Dictionary<Guid, int> DirectionCost { get; set; }
    }

    public enum ServiceType
    {
        Move = 0,
        Store = 1,
        Other = 2,
        Luggage = 3
    }
}
 
Why would you not want to use order by? Your current code in post #1 does essentially the same thing but the much harder way. Your code from post #1 would first collect all the values, and find the lowest value. Then it would do a second pass through looking for the entries where there is a matching minimum value, then returns the first matching entry. My code does a single pass through all the entries sorting them, and then returns the first entry.
 
See line 6 in post #2. It will return the first one.
Yes, It actually does this, but I need to save the initial result of output, but this case just outputs:

Order by and min outputs, and I try the first style case to get.
Difference:
{"serviceType":0,"directionCost":{"Key":"a2cd568a-9438-4d4b-acc5-733f651c04a6","Value":1}}
{"serviceType":3,"directionCost":{"Key":"11198b1a-3ead-437b-99d5-e78900971063","Value":4}}
{"serviceType":2,"directionCost":{"Key":"2c2971ac-82cf-4077-9895-383aa4446126","Value":7}}

{"serviceType":0,"directionCost":1}
{"serviceType":3,"directionCost":4}
{"serviceType":2,"directionCost":7}
 
Why would you not want to use order by? Your current code in post #1 does essentially the same thing but the much harder way. Your code from post #1 would first collect all the values, and find the lowest value. Then it would do a second pass through looking for the entries where there is a matching minimum value, then returns the first matching entry. My code does a single pass through all the entries sorting them, and then returns the first entry.
So, If the better way is that your propose - I think, I take yours into consideration.
But if to understand my thread better - if there is one million records in a dictionary, could your proposal be effective?
Anyway, thank you very much.
 
Ok, I think, I am done, let it be an alternative decision.
alternative:
var final2 = modelList.Select(

                o => new

                {

                    serviceType = o.ServiceType,

                    directionCost = o.DirectionCost

                        .FirstOrDefault(el => el.Value == o.DirectionCost.Values.Min())

                }

            );
 
Oh I see you are worried about the cost of sorting which would be O( n log n ) vs O( n ) to scan for the lowest value + O( n ) to find the first matching lowest value. Your correct that doing the two passes would be cheaper.

As for results showing as "Key" and "Value": create a new dictionary like you were attempting. Just add that first entry.
 
Back
Top Bottom