5

I want to store data such as

{
 {"apple",15   }
 {"pear",12.5  }
 {"", 10       }
 {"", 0.45     }
}

Data will be plotted on a bar chart (string will be the legend and double will be the value) Insert order is important. Perfs don't matter. Strings could be duplicated or empty. (values could be duplicated too)

I need to get min and max values (easily if possible) to set the scale.

I use

List<KeyValuePair<string, double>> data = new List<KeyValuePair<string, double>>();
data.Add(new KeyValuePair<string,double>("",i));

Quite boring and unreadable. Is there a cleaner way to do it ?

StringDoubleCollection data = new StringDoubleCollection();
data.add("apple",15);
data.add("",10);

double max = data.values.Max(); 
double min = data.values.Min();

if not how to get the max value of List<KeyValuePair<string, double>> without too much hassle

NameValueCollection looks nice but its a <string,string> I need a <string,double>

GEOCHET
  • 20,623
  • 15
  • 71
  • 98
frenchone
  • 1,269
  • 2
  • 16
  • 25
  • stackoverflow killed my post by not escaping some '> and NameValueCollection is nice but its a I need a – frenchone Sep 21 '11 at 17:05
  • fixed the formatting for you. – Ray Sep 21 '11 at 17:07
  • What you want is basically a dictionary which allows duplicate keys. According to this question: http://stackoverflow.com/questions/146204/duplicate-keys-in-net-dictionaries there's nothing in the Framework that supports it. You'll probably need to make your own generic version of NameValueCollection. – Michael Edenfield Sep 21 '11 at 17:09
  • thank you Ray for fixing – frenchone Mar 22 '12 at 13:27

6 Answers6

5

You could create a class like the following:

class X
{
     public string Name { get; set; }
     public double Value { get; set; }

     // name is an optional parameter (this means it can be used only in C# 4)
     public X(double value, string name = "")
     {
         this.Name = name;
         this.Value = value;
     }

     // whatever
}

And then get maximum and minimum values using LINQ with a selector:

var data = new List<X>();
data.Add(new X(35.0, "Apple"))
data.Add(new X(50.0));

double max = data.Max(a => a.Value);
double min = data.Min(a => a.Value);

EDIT: if the code above still seems unreadable to you try to improve it using an operator for cases in which you want to have just the value.

// Inside X class...
public static implicit operator X(double d)
{
    return new X(d);
}

// Somewhere else...
data.Add(50.0);
as-cii
  • 11,939
  • 4
  • 37
  • 43
  • I'm sorry, I skipped the *duplicates* part when reading. Edited anyway :) – as-cii Sep 21 '11 at 17:10
  • It would be a good idea to just remove the bit about the dictionary rather than letting your edit explain it away. An encapsulating class is a worthy suggestion, so I've voted the answer up. – Anthony Pegram Sep 21 '11 at 17:14
1

Have you looked at LookUp?

The only problem is that it's immutable, so you need to be able to create your collection in one go.

As Anthony Pegram notes, it's a bit of a pain to create one. It depends on where your data is coming from. Have a look at the ToLookup method.

Ray
  • 43,353
  • 23
  • 121
  • 168
  • 1
    The problem with this is that it is not directly instantiable. You start from something else and can then project into this. He would need an intermediary structure first. – Anthony Pegram Sep 21 '11 at 17:07
  • Yeah that's true. But it might still be possible depending on where his data is coming from (no information was provided). It's worth knowing about in any case. – Ray Sep 21 '11 at 17:09
1

If it's worth it for usability (i.e. you're using awkward collections of List<KeyValuePair<string, double>> everywhere, it might just be worth it to implement StringDoubleCollection. It wouldn't be that difficult to wrap the underlying collection with the friendlier syntax you've described in your example.

And, as other comments / answers are suggesting, the Framework doesn't seem to provide a simpler solution that matches all of your requirements...

As for "max value", I assume you mean the Key-Value Pair with the greatest value. It can be retrieved like so:

var max = list.Select(kvp => kvp.Value).Max();
Dan J
  • 15,422
  • 6
  • 47
  • 76
1

Just define your own model class to hold the data instead of depending on a KeyValuePair and everything becomes cleaner:

using System;
using System.Collections.Generic;

public class Fruit
{
    public string Name {get; set;}
    public double Price {get; set;}
}

public class Program
{
    public static void Main()
    {
        List<Fruit> _myFruit = new List<Fruit>();

        _myFruit.Add(new Fruit{Name="apple", Price=15   });
        _myFruit.Add(new Fruit{Name="pear", Price=12.5  });
        _myFruit.Add(new Fruit{Name="",  Price=10       });
        _myFruit.Add(new Fruit{Name="",  Price=0.45     });

        // etc...
    }
}
Paul Sasik
  • 73,575
  • 18
  • 144
  • 180
1

To determine which data structure you really want, lets look at your usage patterns.

  • Insert order matters.
  • You don't access your items by key.
  • You want min and max.

A heap offers min or max, but doesn't preserve order. A hash based dictionary also doesn't preserve order. A List is actually a good choice for your data structure. It is available and offers excellent support.

You can prettify your code by defining classes for both the data structure and your bar data. And you can add min/max functionality to the collection. Note: I didn't use the Linq Min/Max functions, because they return the minimum value, not the minimum element.

public class BarGraphData {
    public string Legend { get; set; }
    public double Value { get; set; }
}

public class BarGraphDataCollection : List<BarGraphData> {
    // add necessary constructors, if any

    public BarGraphData Min() {
        BarGraphData min = null;
        // finds the minmum item
        // prefers the item with the lowest index
        foreach (BarGraphData item in this) {
            if ( min == null )
                min = item;
            else if ( item.Value < min.Value )
                min = item;
        }
        if ( min == null )
            throw new InvalidOperationException("The list is empty.");
        return min;
    }

    public BarGraphData Max() {
        // similar implementation as Min
    }
}
Mark
  • 385
  • 1
  • 8
0

What about implementing the StringDoubleCollection to work like you want...

public class StringDoubleCollection
{
    private List<KeyValuePair<string, double>> myValues;
    public List<double> values
    {
        get { return myValues.Select(keyValuePair => keyValuePair.Value).ToList(); }
    }

    public void add(string key, double value)
    {
        myValues.Add(new KeyValuePair<string,double>(key,value));
    }
}
Brian
  • 1