I have an array of Vector3's from the polygon collider. I want to have an array of the indexes of all the Vector3's that are higher then a certain y.
private void Awake()
{
polygonCollider2D = GetComponent<PolygonCollider2D>();
float lowestY = polygonCollider2D.points.Min(x => x.y);
// So in the sentence below I want to have a array of indexes instead of a array of vector3's.
topPoints = polygonCollider2D.points.Where(x => x.y > lowestY).ToArray();
}
Can I do this with Linq?
Yes you can use an overload of Select that includes the index like so
var topPointIndexes = polygonCollider2D.points
.Select((p,i) => new{Point=p, Index=i})
.Where(x => x.Point.y > lowestY)
.Select(x => x.Index)
.ToArray();
Another option is to just create the set of indexes up front
var points = polygonCollider2D.points;
var topPointIndexes = Enumerable.Range(0, points.Count())
.Where(i => points[i].y > lowestY)
.ToArray();
The Select allows you to capture the index.
You can first select indexes and filter with the -1 'filter sentinel' value I used below,
topPointsIndexes = polygonCollider2D.points
.Select((x, index) => (x.y > lowestY) ? index : -1)
.Where(i => i >= 0)
.ToArray();
(or first expand the point and index together, and then filter, as juharr did in his answer)
indexesOfTopPoints = polygonCollider2D.points.Select((x, index) => index)
.Where(x => polygonCollider2D.points[x].y > lowestY).ToArray();
Try it
topPoints = polygonCollider2D.points.Select((x, i) => new {obj = x, i = i}).Where(x => x.y > lowestY).Select(x => x.i).ToArray();
Related
I have a list which contains instanceNumber properties of type int I want to add leading 0 if its value is less than 10. i.e 01,02...09 after that 10,11,12 and goes on etc. I tried following code but it did not work
var sidList = _sidRepository.GetAllList().Where(q=>q.IsDeleted==false).OrderByDescending(q=>q.Id).ToList();
if (sidList.Count > 0)
{
sidList.Where(w => w.InstanceNumber<10).ToList().
ForEach(s => s.InstanceNumber = s.InstanceNumber.Value.ToString("D").Length + 2);
}
Try this:
var sidList = _sidRepository.GetAllList()
.Where(q => !q.IsDeleted)
.OrderByDescending(q => q.Id)
.ToList();
if (sidList.Any())
{
sidList
.Where(w => w.InstanceNumber < 10)
.ToList()
.ForEach(s => s.InstanceNumber = s.InstanceNumber.Value.ToString("00").Length + 2);
}
What I would suggest:
var sidList = _sidRepository.GetAllList()
.Where(q => !q.IsDeleted)
.OrderByDescending(q => q.Id);
if (sidList.Any())
foreach (var item in sidList.Where(i => i.InstanceNumber < 10))
item.InstanceNumber = item.InstanceNumber.Value.ToString("00").Length + 2;
My suggestion will return a IEnumerable<item> instead of a List<item>, but you can handle it afterwards with a .ToList() if you need it, but you'll benefit from performance, because you'll avoid ToList(ing) two times.
I have a two dimensional array containing objects of type MyObj.
private MyObj[,] myObjs = new MyObj[maxX, maxY];
I want to get the indices from the array when passing in a matching object. I want to get the x and y value from this array. I can return these two values as a Position object that takes a x and y coordinate.
private Position GetIndices(MyObj obj)
{
for (int x = 0; x < myObjs.GetLength(0); x++)
{
for (int y = 0; y < myObjs.GetLength(1); y++)
{
if (myObjs[x, y] == obj)
{
return new Position(x, y);
}
}
}
}
Is it possible to get this code shorten to some Linq code lines?
But I don't think, it looks nice :)
var result = Enumerable.Range(0, myObjs.GetLength(0))
.Select(x => Enumerable.Range(0, myObjs.GetLength(1)).Select(y => new { x, y }))
.SelectMany(o => o)
.FirstOrDefault(o => myObjs[o.x, o.y] == obj);
Here's another option, if you're interested. It uses an indexer inside the first select and does a little math to find where that index falls inside the two-dimensional array.
var o = new MyObj();
myObjs[1,2] = o;
var p = myObjs.Cast<MyObj>()
.Select((x,i) => Tuple.Create(x,i))
.Where(x => x.Item1 == o)
.Select(x => new Point(x.Item2 / myObjs.GetLength(1), x.Item2 % myObjs.GetLength(1)))
.SingleOrDefault();
Console.WriteLine(p); // prints {X=1,Y=2}
It sorta looks like you're considering the x-coordinate to be the height of the array, and the y-coordinate the width, in which case you'd want to switch it up slightly:
var p = myObjs.Cast<MyObj>()
.Select((x,i) => Tuple.Create(x,i))
.Where(x => x.Item1 == o)
.Select(x => new Point(x.Item2 % myObjs.GetLength(1), x.Item2 / myObjs.GetLength(1)))
.SingleOrDefault();
Console.WriteLine(p); // prints {X=2,Y=1}
I used Point instead of Position since it's built into .NET but you should be able to just swap one for the other.
Yes, that is possible. You can ask Resharper to do the job (loop to linq) for you. After installation, just use the feature.
Using the following linq code, how can I add dense_rank to my results? If that's too slow or complicated, how about just the rank window function?
var x = tableQueryable
.Where(where condition)
.GroupBy(cust=> new { fieldOne = cust.fieldOne ?? string.Empty, fieldTwo = cust.fieldTwo ?? string.Empty})
.Where(g=>g.Count()>1)
.ToList()
.SelectMany(g => g.Select(cust => new {
cust.fieldOne
, cust.fieldTwo
, cust.fieldThree
}));
This does a dense_rank(). Change the GroupBy and the Order according to your need :)
Basically, dense_rank is numbering the ordered groups of a query so:
var DenseRanked = data.Where(item => item.Field2 == 1)
//Grouping the data by the wanted key
.GroupBy(item => new { item.Field1, item.Field3, item.Field4 })
.Where(#group => #group.Any())
// Now that I have the groups I decide how to arrange the order of the groups
.OrderBy(#group => #group.Key.Field1 ?? string.Empty)
.ThenBy(#group => #group.Key.Field3 ?? string.Empty)
.ThenBy(#group => #group.Key.Field4 ?? string.Empty)
// Because linq to entities does not support the following select overloads I'll cast it to an IEnumerable - notice that any data that i don't want was already filtered out before
.AsEnumerable()
// Using this overload of the select I have an index input parameter. Because my scope of work is the groups then it is the ranking of the group. The index starts from 0 so I do the ++ first.
.Select((#group , i) => new
{
Items = #group,
Rank = ++i
})
// I'm seeking the individual items and not the groups so I use select many to retrieve them. This overload gives me both the item and the groups - so I can get the Rank field created above
.SelectMany(v => v.Items, (s, i) => new
{
Item = i,
DenseRank = s.Rank
}).ToList();
Another way is as specified by Manoj's answer in this question - But I prefer it less because of the selecting twice from the table.
So if I understand this correctly, the dense rank is the index of the group it would be when the groups are ordered.
var query = db.SomeTable
.GroupBy(x => new { x.Your, x.Key })
.OrderBy(g => g.Key.Your).ThenBy(g => g.Key.Key)
.AsEnumerable()
.Select((g, i) => new { g, i })
.SelectMany(x =>
x.g.Select(y => new
{
y.Your,
y.Columns,
y.And,
y.Key,
DenseRank = x.i,
}
);
var denseRanks = myDb.tblTestReaderCourseGrades
.GroupBy(x => new { x.Grade })
.OrderByDescending(g => g.Key.Grade)
.AsEnumerable()
.Select((g, i) => new { g, i })
.SelectMany(x =>
x.g.Select(y => new
{
y.Serial,
Rank = x.i + 1,
}
));
I have a IList<string>() which holds some string values, and there could be duplicated items in the list. What I want is to append a index number to end of the string to eliminate the duplication.
For example, I have these values in my list: StringA, StringB, StringC, StringA, StringA, StringB. And I want the result looks like: StringA1, StringB1, StringC, StringA2, StringA3, StringB2. I need to retain the original order in list.
Is there a way I can just use one Lambda expression?
You are looking for something like this:
yourList.GroupBy(x => x)
.SelectMany(g => g.Select((x,idx) => g.Count() == 1 ? x : x + idx))
.ToList();
Edit: If the element order matters, here is another solution:
var counts = yourList.GroupBy(x => x).ToDictionary(x => x.Key, x => x.Count());
var values = counts.ToDictionary(x => x.Key, x => 0);
var list = yourList.Select(x => counts[x] > 1 ? x + ++values[x] : x).ToList();
You can do:
List<string> list = new List<string> { "StringA", "StringB", "StringC", "StringA", "StringA", "StringB" };
var newList =
list.Select((r, i) => new { Value = r, Index = i })
.GroupBy(r => r.Value)
.Select(grp => grp.Count() > 1 ?
grp.Select((subItem, i) => new
{
Value = subItem.Value + (i + 1),
OriginalIndex = subItem.Index
})
: grp.Select(subItem => new
{
Value = subItem.Value,
OriginalIndex = subItem.Index
}))
.SelectMany(r => r)
.OrderBy(r => r.OriginalIndex)
.Select(r => r.Value)
.ToList();
and you will get:
StringA1,StringB1,StringC,StringA2,StringA3,StringB2
If you don't want to preserve order then you can do:
var newList = list.GroupBy(r => r)
.Select(grp => grp.Count() > 1 ?
grp.Select((subItem, i) => subItem + (i + 1))
: grp.Select(subItem => subItem))
.SelectMany(r => r)
.ToList();
This uses some lambda expressions and linq to do it, maintaining the order but I'd suggested a function with a foreach loop and yield return would be better.
var result = list.Aggregate(
new List<KeyValuePair<string, int>>(),
(cache, s) =>
{
var last = cache.Reverse().FirstOrDefault(p => p.Key == s);
if (last == null)
{
cache.Add(new KeyValuePair<string, int>(s, 0));
}
else
{
if (last.Value = 0)
{
last.Value = 1;
}
cache.Add(new KeyValuePair<string, int>(s, last.Value + 1));
}
return cache;
},
cache => cache.Select(p => p.Value == 0 ?
p.Key :
p.Key + p.Value.ToString()));
I have a list List<UserRoles> roles that has this structure
{r:1,u:1,v:3},
{r:1,u:1,v:5},
{r:2,u:1,v:9},
{r:3,u:2,v:10}
I am trying to write a LINQ statement that will filter out only the "r"s that have values 1 & 2 and return a collection of ints/strings of "v"s
This is what I am trying to do and my problem is in the part where I want to transform the into that holds only the corresponding "v"s.
List<Int32> = roles.Where(r => r.r == 1 || r.r == 2)
.Select(i => new Int32{id = i.v});
This doesn't compile with an error that 'id' is unknown.
the end result that I need is this:
List<Int32>
{v:3},
{v:5},
{v:9}
Sound like you need a list of int:
List<int> result = roles.Where(r => r.r == 1 || r.r == 2)
.Select(i => i.v)
.ToList();
In case you have a list of int to filter, you can use Contains method to avoid lots of ||:
var filters = new[] { 1, 2};
List<int> result = roles.Where(r => filters.Contains(r.r))
.Select(i => i.v)
.ToList();
Or maybe you need {v:9}, you can use anonymous type with var keyword:
var result = roles.Where(r => filters.Contains(r.r))
.Select(i => new { i.v })
.ToList();
I guess v is already an int.
So the solution would be as simple as :
var result = roles.Where(r => r.r == 1 || r.r == 2).Select(i => i.v).ToList();
If what you want is an array of anonymous objects, use this:
var res = roles.Where(r => r.r == 1 || r.r == 2).Select(i => new{i.v}).ToList();
This would produce a list of objects with a single property called v.
If you are looking for a list of integers, and v is an int in the original class, use this:
var res = roles.Where(r => r.r == 1 || r.r == 2).Select(i => i.v ).ToList();
// note that there's no new here ^^^^^
You've used an annoymous type but then added Int32 in front of it which is illegal.
List<Int32> results = roles.Where(r => r.r == 1 || r.r == 2)
.Select(i => new { i.v }).ToList();