什么是最(性能)高效且可读的“拆分”方式?基于条件的通用列表?

发布于 2024-11-09 04:26:28 字数 511 浏览 0 评论 0原文

(高度简化的示例) 我有一个通用的字符串列表:

var strings = new List<string> { "abc", "owla", "paula", "lala", "hop" };

我正在寻找最有效的方法来将此列表拆分为包含满足条件的元素的列表和不满足相同条件的元素的列表。

Func<string, bool> condition = s => s.IndexOf("o") > -1;
Predicate<string> kickOut = s => s.IndexOf("o") > -1;
var stringsThatMeetCondition = strings.Where(condition);
strings.RemoveAll(kickOut);
var stringsThatDontMeetCondition = strings;

有没有办法只循环原始列表一次来做到这一点?

(highly simplified example)
I have a generic list of strings:

var strings = new List<string> { "abc", "owla", "paula", "lala", "hop" };

I'm looking for the most efficient way to split this list into a list with elements that meet a condition and a list of elements that don't meet that same condition.

Func<string, bool> condition = s => s.IndexOf("o") > -1;
Predicate<string> kickOut = s => s.IndexOf("o") > -1;
var stringsThatMeetCondition = strings.Where(condition);
strings.RemoveAll(kickOut);
var stringsThatDontMeetCondition = strings;

Is there a way to do this with looping only once through the original list?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

那小子欠揍 2024-11-16 04:26:28

使用一些 linq:

var matches = list.Select(s => s.IndexOf("o") > -1).ToList();
var notMatches = list.Except(matches).ToList();
list.Clear();
list.AddRange(matches);

更新: 正如评论中提到的,请小心改变列表,因为 linq 方法尝试按需,它们不会迭代列表,直到您开始查看 IEnumerable。但就我而言,我调用 ToList,这实际上导致它遍历整个项目列表。

Use some linq:

var matches = list.Select(s => s.IndexOf("o") > -1).ToList();
var notMatches = list.Except(matches).ToList();
list.Clear();
list.AddRange(matches);

Update: as has been mentioned in the comments, be careful mutating the list as linq methods try to be on-demand, they will not iterate the list until you start looking into the IEnumerable. However in my case, I call ToList, which effectively causes it to run through the entire list of items.

掀纱窥君容 2024-11-16 04:26:28

这样就可以了:

IEnumerable<T> FilterAndRemove(this List<T> list, Func<T, bool> pred)
{
  List<T> filtered = new List<T>();
  int i = 0;
  while(i < list.Count)
  {
     if (pred(list[i]))
     {
        filtered.Add(list[i]);
        list.RemoveAt(i);
     }
     else
     { 
        ++i;
     }
  }
  return list;
}

但我相信你已经想到了类似的事情。您能否以您寻求的效率更新您的答案?

请注意,在原始列表上使用 pred!pred 进行两次过滤仍然是 O(n),而且效率一点也不低。特别是考虑到您将获得两个结果集的惰性求值的全部好处。另请参阅罗布的回答。

该算法的时间复杂度为 O(n^2)。

您还可以将它们收集到一个新列表中,并在返回之前将它们复制到输入列表中,而不是删除每个元素。这也将为您带来 O(n)。

O(n) 的另一种选择是切换到链表。

This would do it:

IEnumerable<T> FilterAndRemove(this List<T> list, Func<T, bool> pred)
{
  List<T> filtered = new List<T>();
  int i = 0;
  while(i < list.Count)
  {
     if (pred(list[i]))
     {
        filtered.Add(list[i]);
        list.RemoveAt(i);
     }
     else
     { 
        ++i;
     }
  }
  return list;
}

But am sure you have already thought of something similar. Can you please update your answer with the kind of efficiency that you seek?

Note that two filtering runs with pred and !pred over the original list would still be O(n) and not at all inefficient. Especially considering that you'd get the full benefit of lazy evaluation for both result sets. See also Rob's answer.

This algorithm is in O(n^2).

Instead removing each element, you can also collect them in a new list and copy them over to the input list before returning. This will also get you O(n).

One more option for O(n) would be switching to a linked list.

挽你眉间 2024-11-16 04:26:28

为什么不直接使用

var stringsThatMeetCondition = strings.Where(condition);
var stringsThatDontMeetCondition = strings.Where(x => !condition(x));

当然,您最终会将条件应用于列表中的每个元素两次。为了避免这种情况,您可能需要编写一个通用的拆分函数,但这不会那么简洁。

Why not just use

var stringsThatMeetCondition = strings.Where(condition);
var stringsThatDontMeetCondition = strings.Where(x => !condition(x));

Of course, you end up applying the condition to each element in the list twice. To avoid this you might want to write a generic splitting function, which wouldn't be as neat.

|煩躁 2024-11-16 04:26:28
Func<string, bool> condition = ...;
var groupedStrings = strings.GroupBy(condition)
var stringsMeetingCondition = groupedStrings.FirstOrDefault(g => g.Key);
var stringsNotMeetingCondition = groupedStrings.FirstOrDefault(g => !g.Key);
Func<string, bool> condition = ...;
var groupedStrings = strings.GroupBy(condition)
var stringsMeetingCondition = groupedStrings.FirstOrDefault(g => g.Key);
var stringsNotMeetingCondition = groupedStrings.FirstOrDefault(g => !g.Key);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文