C#在列表中快速查找连续值
我正在做一个绘制鼠标轨迹的项目。C#在列表中快速查找连续值,c#,list,linq,C#,List,Linq,我正在做一个绘制鼠标轨迹的项目。MouseInfo类的定义如下: public class MouseInfo { public readonly long TimeStamp; public readonly int PosX; public readonly int PosY; public int ButtonsDownFlag; } 我需要找到一种方法从列表中提取鼠标位置,其中按钮下旗至少有2个连续的1s,并将它们组合在一起,以便我能够区分单击和拖动,然
MouseInfo
类的定义如下:
public class MouseInfo {
public readonly long TimeStamp;
public readonly int PosX;
public readonly int PosY;
public int ButtonsDownFlag;
}
我需要找到一种方法从列表
中提取鼠标位置,其中按钮下旗
至少有2个连续的1
s,并将它们组合在一起,以便我能够区分单击和拖动,然后将其用于打印
我目前的做法是遍历列表,然后将找到的值一个接一个地添加到其他列表中,这非常缓慢、昂贵,而且代码看起来很凌乱。我想知道有没有更优雅的方法?Linq
会有帮助吗
例如,我录制了以下内容:
(t1, x1, y1, 0)
(t2, x2, y2, 1)
(t3, x3, y3, 1)
(t4, x4, y4, 0)
(t5, x5, y5, 1)
(t6, x6, y6, 0)
(t7, x7, y7, 1)
(t8, x8, y8, 1)
(t9, x9, y9, 1)
(ta, xa, ya, 0)
(tb, xb, yb, 2) <- Yes, ButtonDownFlag can be 2 for RightClicks or even 3 for both buttons are down
(tc, xc, yc, 0)
(td, xd, yd, 2)
(te, xe, ye, 2)
及
注:
时间戳
更改为第一个元素的时间戳
,以便在以后的绘图中进行分组时间戳
,但我关心按钮关闭标志
ButtonDownFlag
,第二个列表中是否存在TimeStamp
有一种方法可以使用LINQ来完成此操作,它将为属于拖动序列的所有事件生成一个列表,并为单个单击事件生成一个单独的列表
List<MouseInfo> mainList = new List<MouseInfo>();
//populate mainList with some data...
List<MouseInfo> dragList = mainList.Where
(
// check if the left click is pressed
x => x.ButtonsDownFlag == 1
//then check if either the previous or the following elements are also clicked
&&
(
//if this isnt the first element in the list, check the previous one
(mainList.IndexOf(x) != 0 ? mainList[mainList.IndexOf(x) - 1].ButtonsDownFlag == 1 : false)
//if this isnt the last element in the list, check the next one
|| (mainList.IndexOf(x) != (mainList.Count - 1) ? mainList[mainList.IndexOf(x) + 1].ButtonsDownFlag == 1 : false)
)
).ToList();
List<MouseInfo> clickList = mainList.Where
(
// check if the left/right or both click is pressed
x => (x.ButtonsDownFlag == 1 || x.ButtonsDownFlag == 2 || x.ButtonsDownFlag == 3)
//then make sure that neither of the previous or following elements are also clicked
&&
(mainList.IndexOf(x) != 0 ? mainList[mainList.IndexOf(x) - 1].ButtonsDownFlag != 1 : true)
&&
(mainList.IndexOf(x) != (mainList.Count - 1) ? mainList[mainList.IndexOf(x) + 1].ButtonsDownFlag != 1 : true)
).ToList();
List mainList=新列表();
//用一些数据填充mainList。。。
List dragList=mainList.Where
(
//检查是否按下了左键
x=>x.ButtonsDownFlag==1
//然后检查是否也单击了上一个或以下元素
&&
(
//如果这不是列表中的第一个元素,请选中前一个元素
(mainList.IndexOf(x)!=0?mainList[mainList.IndexOf(x)-1]。ButtonsDownFlag==1:false)
//如果这不是列表中的最后一个元素,请选中下一个元素
||(mainList.IndexOf(x)!=(mainList.Count-1)?mainList[mainList.IndexOf(x)+1]。ButtonsDownFlag==1:false)
)
).ToList();
列表单击列表=主列表。其中
(
//检查是否按下了左/右或两次单击
x=>(x.ButtonsDownFlag==1 | | x.ButtonsDownFlag==2 | | x.ButtonsDownFlag==3)
//然后确保前面或后面的元素都没有被单击
&&
(mainList.IndexOf(x)!=0?mainList[mainList.IndexOf(x)-1].ButtonsDownFlag!=1:true)
&&
(mainList.IndexOf(x)!=(mainList.Count-1)?mainList[mainList.IndexOf(x)+1]。ButtonsDownFlag!=1:true)
).ToList();
这种方法的局限性在于不能用相同的时间戳“标记”每个拖拽序列
另一种选择是在数据捕获点执行此逻辑。捕获每个数据点时,如果其具有“ButtonDown”值,请检查上一个数据点。如果该数据点也是一个“按钮向下”的数据点,则将其同时添加到“dragList”中,否则将其添加到“clickList”中。
对于这个选项,我还想添加一些逻辑来分离不同的拖动序列。如果您通过更改后续时间点的时间戳来实现这一点,那么我会尝试将您的“dragList”创建为字典。将每个拖拽序列放入不同的不同键。我不认为这太容易理解,但它类似于在APL中处理这个问题的方式(我使用Excel来解决)。我也不会保证这有多快——一般来说,
foreach
比LINQ快,即使只快一小部分
使用扩展方法实现APL的扫描和压缩运算符,并在IEnumerable
s中追加/前置:
public static IEnumerable<TResult> Scan<T, TResult>(this IEnumerable<T> src, TResult seed, Func<TResult, T, TResult> combine) {
foreach (var s in src) {
seed = combine(seed, s);
yield return seed;
}
}
public static IEnumerable<T> Compress<T>(this IEnumerable<bool> bv, IEnumerable<T> src) {
var srce = src.GetEnumerator();
foreach (var b in bv) {
srce.MoveNext();
if (b)
yield return srce.Current;
}
}
public static IEnumerable<T> Prepend<T>(this IEnumerable<T> rest, params T[] first) => first.Concat(rest);
public static IEnumerable<T> Append<T>(this IEnumerable<T> rest, params T[] last) => rest.Concat(last);
完成后,拖动
包含一个列表
,其中每个子列表都是一个拖动。您可以使用SelectMany
而不是最后一个(外部)Select
来获得一个简单的列表。
单击
将包含一个只需单击的列表
注意,我缩写了MouseInfo
字段名
顺便说一句,使用for
循环要快得多:
var inDrag = false;
var drags = new List<MouseInfo>();
var clicks = new List<MouseInfo>();
var beginTime = 0L;
for (var i = 0; i < moves.Count; ++i) {
var curMove = moves[i];
var wasDrag = inDrag;
inDrag = curMove.b == 1 && (inDrag || (i + 1 < moves.Count ? moves[i + 1].b == 1 : false));
if (inDrag) {
if (wasDrag)
drags.Add(new MouseInfo { t = beginTime, x = curMove.x, y = curMove.y, b = curMove.b });
else {
drags.Add(curMove);
beginTime = curMove.t;
}
}
else {
if (curMove.b != 0)
clicks.Add(curMove);
}
}
var inDrag=false;
var drags=新列表();
var clicks=newlist();
var beginTime=0L;
对于(变量i=0;i
只是想分享一些知识-我发现GroupNext很好地解决了我的问题(还有一些用于后期绘图的tweek)
性能肯定不是最好的(与for循环相比),但我觉得代码更优雅
参考:
公共静态类LocalExtensions{
公共静态IEnumerable组(
这是一个数不清的来源,
Func键选择器){
TKey last=默认值(TKey);
bool-haveLast=false;
列表=新列表();
foreach(源中的TSource s){
TKey k=按键选择器;
if(haveLast){
如果(!k.Equals(last)){
返回新的相邻组(列表,最后一个);
public static IEnumerable<TResult> Scan<T, TResult>(this IEnumerable<T> src, TResult seed, Func<TResult, T, TResult> combine) {
foreach (var s in src) {
seed = combine(seed, s);
yield return seed;
}
}
public static IEnumerable<T> Compress<T>(this IEnumerable<bool> bv, IEnumerable<T> src) {
var srce = src.GetEnumerator();
foreach (var b in bv) {
srce.MoveNext();
if (b)
yield return srce.Current;
}
}
public static IEnumerable<T> Prepend<T>(this IEnumerable<T> rest, params T[] first) => first.Concat(rest);
public static IEnumerable<T> Append<T>(this IEnumerable<T> rest, params T[] last) => rest.Concat(last);
// create a terminal MouseInfo for scanning along the moves
var mterm = new MouseInfo { t = 0, x = 0, y = 0, b = 4 };
// find the drags for button 1 except the first row
var bRestOfDrag1s = moves.Append(mterm).Zip(moves.Prepend(mterm), (dm, em) => dm.b == 1 && dm.b == em.b).ToList();
// number the drags by finding the drag beginnings
var iLastDragNums = bRestOfDrag1s.Zip(bRestOfDrag1s.Skip(1), (fm, gm) => (!fm && gm)).Scan(0, (a, hm) => hm ? a + 1 : a).ToList();
// find the drags
var bInDrag1s = bRestOfDrag1s.Zip(bRestOfDrag1s.Skip(1), (fm, gm) => (fm || gm));
// number each drag row by its drag number
var dnmiDrags = bInDrag1s.Compress(Enumerable.Range(0, moves.Count)).Select(idx => new { DragNum = iLastDragNums[idx], mi = moves[idx] });
// group by drag number and smear first timestamp along drags
var drags = dnmiDrags.GroupBy(dnmi => dnmi.DragNum)
.Select(dnmig => dnmig.Select(dnmi => dnmi.mi).Select(mi => new MouseInfo { t = dnmig.First().mi.t, x = mi.x, y = mi.y, b = mi.b }).ToList()).ToList();
var clicks = bInDrag1s.Select(b => !b).Compress(moves).Where(mi => mi.b != 0).ToList();
var inDrag = false;
var drags = new List<MouseInfo>();
var clicks = new List<MouseInfo>();
var beginTime = 0L;
for (var i = 0; i < moves.Count; ++i) {
var curMove = moves[i];
var wasDrag = inDrag;
inDrag = curMove.b == 1 && (inDrag || (i + 1 < moves.Count ? moves[i + 1].b == 1 : false));
if (inDrag) {
if (wasDrag)
drags.Add(new MouseInfo { t = beginTime, x = curMove.x, y = curMove.y, b = curMove.b });
else {
drags.Add(curMove);
beginTime = curMove.t;
}
}
else {
if (curMove.b != 0)
clicks.Add(curMove);
}
}
public static class LocalExtensions {
public static IEnumerable<IGrouping<TKey, TSource>> GroupAdjacent<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector) {
TKey last = default(TKey);
bool haveLast = false;
List<TSource> list = new List<TSource>();
foreach (TSource s in source) {
TKey k = keySelector(s);
if (haveLast) {
if (!k.Equals(last)) {
yield return new GroupOfAdjacent<TSource, TKey>(list, last);
list = new List<TSource>();
list.Add(s);
last = k;
} else {
list.Add(s);
last = k;
}
} else {
list.Add(s);
last = k;
haveLast = true;
}
}
if (haveLast)
yield return new GroupOfAdjacent<TSource, TKey>(list, last);
}
}
class GroupOfAdjacent<TSource, TKey> : IEnumerable<TSource>, IGrouping<TKey, TSource> {
public TKey Key { get; set; }
private List<TSource> GroupList { get; set; }
IEnumerator IEnumerable.GetEnumerator() {
return ((IEnumerable<TSource>)this).GetEnumerator();
}
IEnumerator<TSource> IEnumerable<TSource>.GetEnumerator() {
foreach (TSource s in GroupList)
yield return s;
}
public GroupOfAdjacent(List<TSource> source, TKey key) {
GroupList = source;
Key = key;
}
}
private class MouseInfo {
public readonly long TimeStamp;
public readonly int PosX;
public readonly int PosY;
public int ButtonsDownFlag;
public MouseInfo(long t, int x, int y, int flag) {
TimeStamp = t;
PosX = x;
PosY = y;
ButtonsDownFlag = flag;
}
public override string ToString() {
return $"({TimeStamp:D2}: {PosX:D3}, {PosY:D4}, {ButtonsDownFlag})";
}
}
public Program() {
List<MouseInfo> mi = new List<MouseInfo>(14);
mi.Add(new MouseInfo(1, 10, 100, 0));
mi.Add(new MouseInfo(2, 20, 200, 1));
mi.Add(new MouseInfo(3, 30, 300, 1));
mi.Add(new MouseInfo(4, 40, 400, 0));
mi.Add(new MouseInfo(5, 50, 500, 1));
mi.Add(new MouseInfo(6, 60, 600, 0));
mi.Add(new MouseInfo(7, 70, 700, 1));
mi.Add(new MouseInfo(8, 80, 800, 1));
mi.Add(new MouseInfo(9, 90, 900, 1));
mi.Add(new MouseInfo(10, 100, 1000, 0));
mi.Add(new MouseInfo(11, 110, 1100, 2));
mi.Add(new MouseInfo(12, 120, 1200, 0));
mi.Add(new MouseInfo(13, 130, 1300, 2));
mi.Add(new MouseInfo(14, 140, 1400, 2));
var groups = mi.GroupAdjacent(x => x.ButtonsDownFlag);
List<List<MouseInfo>> drags = groups.Where(x => x.Key == 1 && x.Count() > 1).Select(x => x.ToList()).ToList();
foreach (var d in drags)
foreach (var item in d)
Console.Write($"{item} ");
Console.WriteLine();
List<List<MouseInfo>> clicks = groups.Where(x => x.Key > 1 || (x.Key == 1 && x.Count() == 1)).Select(x => x.ToList()).ToList();
foreach (var d in clicks) {
foreach (var item in d)
Console.Write($"{item} ");
Console.WriteLine();
}
}
[MTAThread]
static void Main(string[] args) {
var x = new Program();
Console.ReadLine();
return;
}