【问题标题】:How to find the next element in a generic List?如何在通用列表中找到下一个元素?
【发布时间】:2015-05-16 20:02:21
【问题描述】:

这是我的通用列表:

公共类 TagType { 公共字符串字段标签; 公共 int 位置; }

List<TagType<dynamic>> TagList = new List<TagType<dynamic>>();

TagList.Add(new TagType<dynamic>() { FieldTag = "ID", Position = posIdStr });
TagList.Add(new TagType<dynamic>() { FieldTag = "PT", Position = posPtStr });
TagList.Add(new TagType<dynamic>() { FieldTag = "ID", Position = posIdStr });
TagList.Add(new TagType<dynamic>() { FieldTag = "EC", Position = posECStr });

我正在尝试获取紧随其后的 FieldTag 的位置值(例如:PT)。 我该怎么做?

【问题讨论】:

  • 如果你要找的那个不在里面怎么办?如果是最后一项呢?如果有多个怎么办?你试过什么?
  • 我想知道为什么没有人建议使用LinkedList&lt;TagType&lt;dynamic&gt;&gt; 而不是List&lt;TagType&lt;dynamic&gt;&gt;,那么您将在每个条目上都有.Next 和.Previous 属性

标签: c# asp.net .net linq generics


【解决方案1】:

你找到PT的索引并加1? (但记得检查 index + 1

// Find the index of PT
int ix = TagList.FindIndex(x => x.FieldTag == "PT");

// index found
if (ix != -1)
{
    // Check that index + 1 < the length of the List
    if (ix + 1 < TagList.Count)
    {
        var position = TagList[ix + 1]; // Add 1
    }
}

【讨论】:

    【解决方案2】:

    如果你想用FieldTag PT 在每个项目之后获取下一个元素的Position,那么你可以用LINQ在一两行中解决它:

    var resultTag = TagList.SkipWhile(x => x.FieldTag != "PT").Skip(1).FirstOrDefault();
    var resultPosition = resultTag == null ?  0 : resultTag.Position;
    

    补充:
    如果您想将其转换为 int,则只需明确地转换即可。

    var resultTag = TagList.SkipWhile(x => x.FieldTag != "PT").Skip(1).FirstOrDefault();
    int resultPosition = resultTag == null ?  0 : (int)resultTag.Position;
    

    【讨论】:

    • 这必须是 TagsList.Where().SKip。 First() 会立即返回标签列表
    • @MichalCiechan 已更新。
    • 您的FirstOrDefautlt.Position 也会抛出 NullReference 异常,最好先使用 ;-) 或者在调用 .Position 之前获取它并检查 null
    • 另外你跳过 while 返回第一个字符串 ;-) 当它遇到 ID 的 FieldTag 时,它看起来没问题,它不是 PT,所以你需要使用 where
    • _tagList.SkipWhile(x =&gt; x.FieldTag != "PT").Skip(1).First().Position;
    【解决方案3】:

    不幸的是,每次您进行搜索时,您都必须遍历列表,找到您要查找的字段标签,然后转到下一个元素并获取位置值。 e..:查找解决方案的 O(n):

        private static object SearchPosition(List<TagType<object>> tagList, string fieldTag)
        {
            var i = tagList.FindIndex(x => x.FieldTag == "PT");
    
            if (i >= 0 && i < tagList.Count)
            {
                return tagList[i + 1].Position;
            }
        }
    

    并测试:

        [Test]
        public void FieldTagTest()
        {
            var res = SearchPosition(_tagList, "PT");
    
            res.ToString().Should().Be("ID2");
        }
    

    如果您的列表不经常更改,您应该构建一个Dictionary&lt;string,int&gt;,其中FieldTag 为Key,列表索引位置为value。当然,每次修改列表时,都需要重新构建此索引。

    O(1) 解决方案是:

        private static object SearchPositionUsingIndex(List<TagType<object>> tagList, string fieldTag)
        {
            // You would save this index, and build it only once, 
            // or rebuild it whenver something changes.
            // you could implement custom index modifications.
            var index = BuildIndex(tagList);
    
            int i;
            if (!index.TryGetValue(fieldTag, out i)) return null;
            if (i + 1 >= tagList.Count) return null;
            return tagList[i + 1].Position;
        }
    
        private static Dictionary<string, int> BuildIndex(List<TagType<object>> tagList)
        {
            var index = new Dictionary<string, int>();
    
            for (int i = 0; i < tagList.Count; i++)
            {
                var tag = tagList[i];
                if (!index.ContainsKey(tag.FieldTag)) index.Add(tag.FieldTag, i);
            }
            return index;
        }
    

    并测试:

        [Test]
        public void FieldTagTestUsingIndex()
        {
            var res = SearchPositionUsingIndex(_tagList, "PT");
    
            res.ToString().Should().Be("ID2");
        }
    

    或者您可以使用 1 行 LINQ 方法,这也是 O(n):

        [Test]
        public void FieldTagTestLinq()
        {
            var res = SearchUsingLinq();
    
            res.ToString().Should().Be("ID2");
        }
    
        private object SearchUsingLinq()
        {
            var p = _tagList.SkipWhile(x => x.FieldTag != "PT").Skip(1).FirstOrDefault();
            return p != null ? p.Position : null;
        }
    

    测试设置

    public class SO29047477
    {
        private List<TagType<object>> _tagList;
    
        [SetUp]
        public void TestSetup()
        {
            _tagList = new List<TagType<dynamic>>();
    
            _tagList.Add(new TagType<dynamic>() { FieldTag = "ID", Position = "ID1"});
            _tagList.Add(new TagType<dynamic>() { FieldTag = "PT", Position = "PT1" });
            _tagList.Add(new TagType<dynamic>() { FieldTag = "ID", Position = "ID2" });
            _tagList.Add(new TagType<dynamic>() { FieldTag = "EC", Position = "EC1" });
    
        }
    }
    

    【讨论】:

    • 过早的优化(也许O(n) 不是问题,因为n 保持很小,并且给定一次查找,您的解决方案仍然是O(n),因为您必须构建字典),更糟糕的是可维护性(因为更多的代码并且必须保持列表和字典同步),并且这假设每个 FieldTag 只出现一次或只应该找到第一个。
    • 我同意是的,但是如果他给出了一小段代码,并且有 1000 种标签类型,被多次搜索,那么值得把这个答案放在那里。是的,对于 1 次查找,它没有任何意义。但是我已经看到小数据集随着时间的推移增长到大数据集;-)
    • 确实,过早的优化是万恶之源。但是,如果他的 TagList 来自外部来源,并且可能会增长,那么如果他需要进行多次搜索,他迟早会遇到问题。
    • 是的,但就像在大多数情况下一样,问题太宽泛了,正确的答案是“你的方法是错误的,重新开始”。我不怪你,只是不得不指出这些事情。
    【解决方案4】:
    public Position? GetNextPosition(string FieldTagVal)
    {
        bool returnNext = false;
        foreach(TagType t in TagList) 
        {
           if (returnNext) return t.Position;
           if (t.FieldTag == FieldTagVal) returnNext = true;
        }
        return null;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-07-07
      • 2013-04-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-28
      相关资源
      最近更新 更多