我怎样才能优雅地编写一个函数来从IEnumerable中获取唯一属性(如果存在的话)?

时间:2013-12-08 17:02:23

标签: c#

我想编写一个通过IEnumerable的函数。对于IEnumerable中的每个项目,它获取一个枚举属性。如果IEnumerable中的所有内容都具有该属性的相同值,则返回该值。否则,它返回null。我可以做到这一点,但不是优雅的。我可以使用Linq表达式吗?请参阅下面的UniqueOption功能。

namespace Play
{
public enum Option
{
    Tom,
    Dick,
    Harry
}

public class OptionHolder
{
    public Option Option { get; set; }

    public override string ToString()
    {
        return Option.ToString();
    }
}

public class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    private static void Main()
    {
        Program p1 = new Program(Option.Tom, Option.Dick, Option.Harry);
        Console.WriteLine("1: "+p1.UniqueOption());     //should be null
        Program p2 = new Program(Option.Dick, Option.Dick, Option.Dick);
        Console.WriteLine("2: " + p2.UniqueOption());   //should be Dick
        Program p3 = new Program(Option.Harry);         
        Console.WriteLine("3: " + p3.UniqueOption());   //should be Harry
    }

    public Program(params Option[] options)
    {
        optionList = new List<OptionHolder>();
        foreach (Option option in options)
        {
            OptionHolder holder = new OptionHolder();
            holder.Option = option;
            optionList.Add(holder);
        }
    }

    /**
     * If all the OptionHolders in the Holders property have the same Option, return this.
     * Otherwise (there are no OptionHolders, or there is more than one but they hold different Options), return null.
     */
    public Option? UniqueOption()
    {
        Option? option = null;

        foreach(OptionHolder holder in optionList) {
            Option o = holder.Option;
            if (option == null)
            {
                option = o;
            }
            else if (option != o)
            {
                return null;
            }
        }
        return option;
    }

    private List<OptionHolder> optionList;

    public IEnumerable<OptionHolder> Holders
    {
        get { return optionList; }
    }

    public override string ToString()
    {
        return String.Join(",", optionList);
    }
}

}

6 个答案:

答案 0 :(得分:2)

如果我理解正确,那么您可以使用Linq的Distinct方法。

public Option? UniqueOption()
{
    var distinct = optionList.Select(x=> x.Option).Distinct();
    if(distinct.Count() == 1)
    {
        return distinct.First();
    }
    return null;
}

public Option? UniqueOptionOptimized()
{
    HashSet<Option> set = new HashSet<Option>();
    foreach (var item in optionList)
    {
        if (set.Add(item.Option) && set.Count > 1)
        {
            return null;
        }
    }

    if (set.Count == 1)
        return set.First();
    else
        return null;
}

public Option? UniqueOptionOptimized2()
{
    using(var distinctEnumerator = optionList.Select(x => x.Option).Distinct().GetEnumerator())
    {
        if(distinctEnumerator.MoveNext())
        {
            var firstOption = distinctEnumerator.Current;
            if(!distinctEnumerator.MoveNext())
                return firstOption;
        }
    }
    return null;
}

答案 1 :(得分:1)

使用Distinct()Take(2)一旦找到两个不同的选项就停止枚举列表,然后检查是否找到了一个不同的选项(而不是零或两个):

public Option? UniqueOption()
{
    Option[] options = optionList.Select(holder => holder.Option).Distinct().Take(2).ToArray();
    return options.Length == 1 ? options[0] : (Option?)null;
}

UPDATE:要检查实际枚举的值数,可以使用以下帮助程序方法:

public static IEnumerable<T> Trace<T>(IEnumerable<T> values)
{
    foreach (T value in values)
    {
        Console.WriteLine("Yielding {0}...", value);
        yield return value;
    }
}

这样称呼:

Option[] options = Trace(optionList).Select(holder => holder.Option).Distinct().Take(2).ToArray();

这表明p1仅枚举TomDick,而不是Harry

答案 2 :(得分:0)

以下功能应符合您的需要。但是,我尝试使用更简单的应用程序,但您应该能够按照自己的意愿进行安排。

    public Option? UniqueOption(params Option[] args)
    {
        if (args == null)
            return null;  //or maybe throws?

        var d = args.Distinct().ToArray();
        return d.Length != 1
            ? d[0]
            : new Nullable<Option>();
    }

答案 3 :(得分:0)

[STAThread]
private static void Main()
{

    Option tested = Option.Paul;
    Program p4 = new Program(Option.Paul, Option.Paul, Option.Paul);
    Console.WriteLine("4: " + p4.UniqueOption(tested));

}
public Option? UniqueOption(Option tested)
{
    if (optionList.All(o => o.Option == tested))
        return tested;

    return null;
}

答案 4 :(得分:0)

另一种变体:

public Option? UniqueOption()
{
    if(!optionList.Any()) return null;
    var first = optionList.First();
    return optionList.All(a => first.Option == a.Option) ? (Option?)first.Option : null;
}

答案 5 :(得分:0)

我最终使用的东西与我原来的解决方案没有什么不同。不过,当我得到更多时间时,我会再看看这个。

public Option? UniqueOption()
{            
        IEnumerator<OptionHolder> enumerator = Holders.GetEnumerator();
        bool hasMoreElements;
        Option? result=null;
        do
        {
           hasMoreElements = enumerator.MoveNext();
           if (hasMoreElements)
           {
              Option? option = enumerator.Current.Option;
              result = (result != null && result != option) ? null : option; 
           }
        } 
        while (hasMoreElements && result != null);
        return result; 
}