Mohsen
Mohsen

Reputation: 247

How to get the Key of an item in the array of dictionary based on its value?

The following method print this:

Minimum Temperature is 16
Maximum Temperature is 27
The Average Temperature is 22 

Now I want in addition to temperatures I also have the days in which the temperatures was maximum and minimum like:

Minimum Temperature is 16 on Day 6
Maximum Temperature is 27 on Day 8
The Average Temperature is 22

Here is the method which inserts Day and Temperature as dictionary parameters into an array and pass them to a method which determines the min,max, average.

the min and max are int values of the dictionary ,my question is that how we can determine the related string day based on these values?

 // if user select January , the January() execute:

 protected void Button1_Click(object sender, EventArgs e)
{
    // assigning Days and Temperatures to Dictionary and making array dictionary

    Dictionary<string, int>[] temperatures = new Dictionary<string, int>[10];
    temperatures[0] = new Dictionary<string, int>();
    temperatures[1] = new Dictionary<string, int>();
    temperatures[2] = new Dictionary<string, int>();
    temperatures[3] = new Dictionary<string, int>();
    temperatures[4] = new Dictionary<string, int>();
    temperatures[5] = new Dictionary<string, int>();
    temperatures[6] = new Dictionary<string, int>();
    temperatures[7] = new Dictionary<string, int>();
    temperatures[8] = new Dictionary<string, int>();
    temperatures[9] = new Dictionary<string, int>();

    temperatures[0].Add("Day1", 22);
    temperatures[1].Add("Day2", 23);
    temperatures[2].Add("Day3", 25);
    temperatures[3].Add("Day4", 26);
    temperatures[4].Add("Day5", 18);
    temperatures[5].Add("Day6", 16);
    temperatures[6].Add("Day7", 17);
    temperatures[7].Add("Day8", 27);
    temperatures[8].Add("Day9", 23);
    temperatures[9].Add("Day10", 24);
    if (DropDownList1.SelectedValue.ToString() == "January")
    {
        January(temperatures);
    }

  //the metthod which calculate min ,max and ..
 private void January(Dictionary<string, int>[] temperatures)
{
    int Minimumtemperture = 40;
    int Maximumtemperture = 0;
    int total = 0;
    int averageTemperatures = 0;
    // this foreach goes through array
    foreach (var temperture in temperatures)
    {

        // this foreach goes throuh dictionary 
        foreach (var degree in temperture)
        {                
            //assigning value of each dictionary to the monthTemp
            int MonthTemps = degree.Value;
            if (MonthTemps < Minimumtemperture)
            {
                Minimumtemperture = MonthTemps;
            }
            if (MonthTemps>Maximumtemperture)
            {
                Maximumtemperture = MonthTemps;
            }
            total = total + MonthTemps;

        }

        int totaltemperature = temperatures.Length;
        averageTemperatures = (total / totaltemperature);

    }

    // printing the result 

    Label1.Text = string.Format("Minimum Temperature is {0}<br/> Maximum Temperature is{1}<br/> The Average Temperature is{2}<br/>", Minimumtemperture, Maximumtemperture, averageTemperatures);

}

Upvotes: 0

Views: 2056

Answers (3)

Slai
Slai

Reputation: 22876

Here is my attempt to make it easier. In the beginning of the file:

using KVP = System.Collections.Generic.KeyValuePair<string, int>; 

and then ( or just replace all "KVP" with "KeyValuePair<string, int>" ) :

KVP[] temperatures = {
    new KVP("Day 1", 22),
    new KVP("Day 2", 23),
    new KVP("Day 2", 25),
    new KVP("Day 2", 26),
    new KVP("Day 2", 18),
    new KVP("Day 2", 16),
    new KVP("Day 2", 17),
    new KVP("Day 2", 27),
    new KVP("Day 2", 23),
    new KVP("Day 2", 24)
};

ILookup<int, string> lookup = temperatures.ToLookup(p => p.Value, p => p.Key);

//string example1 = string.Join(", ", lookup[23]);              // "Day 2, Day 2"
//string example2 = string.Join(", ", lookup[23].Distinct());   // "Day 2"

int min = lookup.Min(p => p.Key);                               // 16
int max = lookup.Max(p => p.Key);                               // 27

//var avg = lookup.Average(p => p.Key);                         // 22.0 (incorrect)
var avg = temperatures.Average(p => p.Value);                   // 22.1

var minDays = string.Join(", ", lookup[min].Distinct());        // "Day 2"
var maxDays = string.Join(", ", lookup[max].Distinct());        // "Day 2"

Seems like Dictionary<string, int[]> (array of temperatures for each day) would be more appropriate in your case, but I used array of key-value pairs to simplify the example.

ILookup<int, string> is similar to Dictionary<int, string[]> where each key (temperature) has multiple values (days).

Upvotes: 1

Georg Patscheider
Georg Patscheider

Reputation: 9463

You are facing this problem because your data structure is inadequate for the job. A Dictionary<string, int>[] will not cut it. So please bear with me and read this long answer ...

Introduce your own classes to group properties together. The class Measurement contains the data.

// single data point
public class Measurement {
    public string Day { get; set; }
    public int Temperature { get; set; }
}

Classes can also encapsulate calculations. The outside only consumes the results, so you can change the underlying implementation. Importantly, this will make your code easier to understand.

The class Month hides the calculations. Calculations are implemented using an ICollection<Measurement> and LINQ.

using System.Collections.Generic;
using System.Linq;

// groups measurements for a certain month and does calculations for this month
public class Month {

    public Month(string name) {
        Name = name;
        Measurements = new List<Measurement>();
    }

    // dictionary key
    public string Name { get; private set; }

    // note that the outside only knows we use an ICollection,
    // that we actually use a List in our implementation is hidden from them
    public ICollection<Measurement> Measurements { get; private set;}

    // to answer your original question:
    // LINQ .Min(m => m.Temperature) and .Max() would only return int
    // sorting will allow you to return the full Measurement, including the day
    // OrderBy runs in O(log(n)), see http://stackoverflow.com/q/3188693/1450855
    public Measurement MinByTemp { get { 
        return Measurements.OrderBy(m => m.Temperature).First(); 
    } }
    public Measurement MaxByTemp { get { 
        return Measurements.OrderBy(m => m.Temperature).Last(); 
    } }

   // more LINQ goodness
   // beware: all these getters cause recalculation each time they are called!
   // on the plus side, the results are always up to date
   public double Average { get { return Measurements.Average(r => r.Temperature); } }   
}

Take a close look at LINQ, this will save you a lot of time writing for loops. Sorting with Orderby() can be extended by implementing IComparable.

This console program shows how to use these classes. It creates the month "January", looks it up by its name and performs the calculations.

public class Program {
    public static void Main() {

        // creating measurements
        var january = new Month("January");
        january.Measurements.Add(new Measurement { Day = "Day1", Temperature = 22 });
        january.Measurements.Add(new Measurement { Day = "Day2", Temperature = 25 });
        january.Measurements.Add(new Measurement { Day = "Day3", Temperature = 26 });
        january.Measurements.Add(new Measurement { Day = "Day4", Temperature = 18 });
        january.Measurements.Add(new Measurement { Day = "Day5", Temperature = 16 });
        january.Measurements.Add(new Measurement { Day = "Day6", Temperature = 17 });

        // finding months by their name
        // using a dictionary will perform this lookup in O(1)
        var months = new Dictionary<string, Month>();
        months.Add(january.Name, january);

        var selectedValue = "January"; // DropDownList1.SelectedValue.ToString();
        if (months.ContainsKey(selectedValue)) {
            var selectedMonth = months[selectedValue];

            // do calculations for the selected month
            // how the calculations are performed is encapsulated
            Measurement max = selectedMonth.MaxByTemp; // call getter only once
            string averageTemp = string.Format("{0:0.00}", selectedMonth.Average);

            // Label1.Text = string.Format(
            Console.WriteLine(selectedMonth.Name + ": Max " + max.Temperature + 
                " (on " + max.Day + ") Avg " +  averageTemp);
        }
        else {
            throw new KeyNotFoundException("Month not found: " + selectedValue);
        }
    }
}

Full example: .Net Fiddle

Upvotes: 3

Abion47
Abion47

Reputation: 24671

You can do this using LINQ:

var dict = new Dictionary<string, int>();
dict.Add("a", 3);
dict.Add("b", 4);
dict.Add("c", 5);
dict.Add("d", 6);

int value = 5; // or whatever
string key = dict.Where(kvp => kvp.Value == value)
                    .Select(kvp => kvp.Key)
                    .FirstOrDefault();

Keep in mind that if you have multiple keys that contain the same value, you might have some issues with collisions. In this case, you can simply replace the FirstOrDefault() with ToArray() to get an array of keys with values that match the given value.

Upvotes: -1

Related Questions