I have a List that contains instances of Beam class. Each of these Beam objects has an Elevation property.
List<Beam> Beams = new List<Beam> {Beam1, Beam2, ...};
public class Beam
{
public double Elevation;
}
Now I want to create a List<double> that contains distinct Elevations.
For example how to write a method that accepts the Beams list as below
var Beam1 = new Beam { Elevation = 320);
var Beam2 = new Beam { Elevation = 320);
var Beam3 = new Beam { Elevation = 640);
var Beam4 = new Beam { Elevation = 0);
List<Beam> Beams = new List<Beam> {Beam1, Beam2, Beam3, Beam4};
And gives this removing the duplicate elevations:
listOfElevations = {0, 320,640}
1) Make Beam implement IComparable:
public class Beam : IComparable
{
public double Elevation; //consider changing this to property, btw.
public int CompareTo(object obj) {
if (obj == null) return 1;
Beam otherBeam = obj as Beam;
return this.Elevation.CompareTo(otherBeam.Elevation);
}
}
2) use Distinct():
var listOfElevations = Beams.Distinct().Select(x=> x.Elevation).ToList();
Use Linq - in particular the Enumerable.Distinct() method is key here:
var listOfElevations = beams.Select(x => x.Elevation) //project to Elevations
.Distinct() // pick only distinct ones
.ToList(); //make it a list
Quite simple using LinQ:
var listOfElevations = Beams.Select(x => x.Elevation).Distinct().ToList();
You're selecting the values of Elevation, choosing the Distinct values, making it to a List since it's your expected output.
List<Beam> Beams = new List<Beam> {Beam1, Beam2, Beam3, Beam4};
var differentBeams = Beams.Select(b => b.Elevation).Distinct().ToList();
Another way using LINQ, this might be useful if you have more than one property and want to get an unique list
beams.GroupBy(x => x.Elevation).Select(g => g.Key);
Related
I would like to sort lists with structures by certain fields/properties of structures. For example there is a structure:
public struct Some
{
public int index;
public DateTime date;
}
Do I have an opportunity to sort such a structure by two parameters simultaneously using the existing methods? Say, at the top of the list of such structures place those that will have the latest date and the largest index. Is it possible to sort simultaneously taking into account these two parameters?
Yes you can using LINQ
with the OrderBy method
OrderBy and ThenBy are the mothods you need.
Example:
list.OrderBy(x => x.index).ThenBy(x => x.date)
If you add Descending you can also sort it the other way round.
Use custom sort comparison function.
Example 1:
public struct MyItem
{
public int index;
public DateTime date;
}
class Program
{
static void Main(string[] args)
{
var items = new MyItem[]{
new MyItem{index=9, date=DateTime.Now},
new MyItem{index=4, date=DateTime.Now},
new MyItem{index=3, date=DateTime.Now},
new MyItem{index=5, date=DateTime.Now},
new MyItem{index=5, date=DateTime.Now + TimeSpan.FromDays(1)},
new MyItem{index=6, date=DateTime.Now},
};
// sort by index, if equal then sort by date
Array.Sort(items, (x, y) =>
{
if (x.index == y.index)
return x.date.CompareTo(y.date);
return x.index.CompareTo(y.index);
});
foreach (var item in items)
Console.WriteLine($"{item.index} {item.date}");
}
}
Example 2:
var items = new List<MyItem>{
new MyItem{index=9, date=DateTime.Now},
new MyItem{index=4, date=DateTime.Now},
new MyItem{index=3, date=DateTime.Now},
new MyItem{index=5, date=DateTime.Now},
new MyItem{index=5, date=DateTime.Now + TimeSpan.FromDays(1)},
new MyItem{index=6, date=DateTime.Now},
};
// sort by index, if equal then sort by date
items.Sort((x, y) => x.index.CompareTo(y.index) == 0 ? x.date.CompareTo(y.date) : x.index.CompareTo(y.index));
Example3: Linq
var items = new List<MyItem>{
new MyItem{index=9, date=DateTime.Now},
new MyItem{index=4, date=DateTime.Now},
new MyItem{index=3, date=DateTime.Now},
new MyItem{index=5, date=DateTime.Now},
new MyItem{index=5, date=DateTime.Now + TimeSpan.FromDays(1)},
new MyItem{index=6, date=DateTime.Now},
};
// sort by index, if equal then sort by date
var newItems = items.OrderBy(x => x.index).ThenBy(x => x.date);
If you want to Sort in place and you don't want to implement IComparable<Some> or IComparer<Some>:
List<Some> myList = ...
...
myList.Sort((left, right) => {
// latest dates on the top (note - for descending sorting)
int result = -left.date.CompareTo(right.date);
// on tie when left and right have the same date we compare indexes
return result == 0
? -left.index.CompareTo(right.index)
: result;
});
In case you have several fragments where you want to sort list in such a way, you can implement a comparer:
public sealed class SomeComparer : IComparer<Some> {
public int Compare(Some left, Some right) {
// latest dates on the top (note - for descending sorting)
int result = -left.date.CompareTo(right.date);
// on tie when left and right have the same date we compare indexes
return result == 0
? -left.index.CompareTo(right.index)
: result;
}
}
Then whenever you want to sort the list:
myList.Sort(new SomeComparer());
Finally, if it's the only order you want to sort Some items, you can make Sort comparable:
public struct Some : IComparable<Some>
{
public int index;
public DateTime date;
//TODO: implement Equals and GetHashCode
public bool CompareTo(Some other) {
int result = -date.CompareTo(other.date);
return result == 0
? -index.CompareTo(other.index)
: result;
}
}
and you can put
myList.Sort();
You can use LINQ to easily sort items in a list by the values contained in a that list.
using System.Linq;
...
myStructs.OrderBy(s => s.index).ThenBy(s => s.date)
Will put everything in order by index. Items with equal index would be sorted by date.
I am working in C#.
How to combine (sum, plus, minus) these class elements in both lists?
class Attribute
{
public AttributeType WhatAttri;
public float amount;
}
enum AttributeType{
maxhp, str, dex, int, wis,,,,
}
Attribute[] attList1;
Attribute[] attList2;
If specific values are like this,
attList1[0] = new Attribute(AttributeType.maxhp, 6)
attList1[1] = new Attribute(AttributeType.str, 4)
attList1[2] = new Attribute(AttributeType.dex, 3)
attList2[0] = new Attribute(AttributeType.str, 9)
attList2[1] = new Attribute(AttributeType.int, 7)
attList2[2] = new Attribute(AttributeType.wis, 5)
I want final result like this, (attList1 values are added, attList2 values are deducted, and also sum(or minus or plus) duplicated AttributeType)
So at above two lists, AttributeType.str is same, so deduct duplicated attList2[0]'s amount variable's value (9) from attList1[1]'s value (4)
and exclude this element from attList2.
So final result should be,
Attribute[] combinedList; (or List<Attribute> combinedList )
combinedList[0] = new Attribute(AttributeType.maxhp, 6)
combinedList[1] = new Attribute(AttributeType.str, -5) (4 - 9)
combinedList[2] = new Attribute(AttributeType.dex, 3)
combinedList[3] = new Attribute(AttributeType.int, -7)
combinedList[4] = new Attribute(AttributeType.wis, -5)
How to achieve this?
Thanks.
var result =
attList2.Select(a => new Attribute(a.WhatAttri, -a.amount)) // line 1
.Concat(attList1) // line 2
.GroupBy(a => a.WhatAttri) // line 3
.Select(g => new Attribute(g.Key, g.Sum(a => a.amount))); // line4
foreach(var a in result)
{
Console.WriteLine($"{a.WhatAttri}: {a.amount}");
}
You want to sum up the counts of the first list and subtract the amounts of the second list. So first I transform the second list to a new list with negative amounts (line 1). then the two lists are joined into one list (line 2).
Then the big line is grouped by type (line 3). and then you have a structure of Key and items, where you create new Attributes by using the key and the sum of the amounts (line 4).
Edit: replaced "Union" in line 2 by "Concat" to avoid dropping duplicate values in case there would be a custom comparer method in class Attribute
The main issue is that you didn't have positive or negative value in the attributes modifier. How whould you boost those attribute? Once it's fix the solution is easy
Add both list with concat, GroupBy AttributeType, and select the values.
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var attributes = new Attribute[] {
new Attribute{WhatAttri=AttributeType.maxhp, amount=6 },
new Attribute{WhatAttri=AttributeType.str, amount=4 },
new Attribute{WhatAttri=AttributeType.dex, amount=3 },
};
//Attribute modifier has to be either positive or negative
var attributesModifier = new Attribute[] {
new Attribute{WhatAttri=AttributeType.str, amount=-9 },
new Attribute{WhatAttri=AttributeType.#int, amount=-7 },
new Attribute{WhatAttri=AttributeType.wis, amount=-5 },
};
var newAttributes = attributes
.Concat(attributesModifier)
.GroupBy(x => x.WhatAttri)
.Select(group =>
new Attribute {
WhatAttri = group.Key,
amount = group.Sum(g => g.amount)
});
newAttributes.Dump();
}
public class Attribute
{
public AttributeType WhatAttri { get; set; }
public float amount { get; set; }
}
public enum AttributeType
{
maxhp, str, dex, #int, wis
}
}
OnLine demo https://dotnetfiddle.net/3C7n7F
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public enum AttributeType
{
maxhp, str, dex, intel, wis,
}
[System.Serializable]
public class Attribute
{
public AttributeType WhatAttri;
public float amount;
public Attribute(AttributeType type, int a)
{
WhatAttri = type;
amount = a;
}
}
public class LinqTest : MonoBehaviour
{
Attribute[] attList1 = new Attribute[3];
Attribute[] attList2 = new Attribute[3];
void Start()
{
attList1[0] = new Attribute(AttributeType.maxhp, 6);
attList1[1] = new Attribute(AttributeType.str, 4);
attList1[2] = new Attribute(AttributeType.dex, 3);
attList2[0] = new Attribute(AttributeType.str, 9);
attList2[1] = new Attribute(AttributeType.intel, 7);
attList2[2] = new Attribute(AttributeType.wis, 5);
Calcul();
}
void Calcul()
{
var result = attList2
.Select(a => new Attribute(a.WhatAttri, -(int)a.amount)) // line 1
.Union(attList1) // line 2
.GroupBy(a => a.WhatAttri) // line 3
.Select(g => new Attribute(g.Key, g.Sum(a => (int)a.amount))); // line4
foreach (var a in result)
{
Debug.Log($"{a.WhatAttri}: {a.amount}");
}
}
}
This is final result of testing above code by answers.
Using Unity engine.
So i have the following List:
List<AttackStyle> AttackStyles = new List<AttackStyle>();
With the following types of objects:
AttackStyle lStyle = new AttackStyle();
lStyle.Name = "Attack Achilles";
lStyle.ParameterID = 0;
lStyle.Forward = Vector3.forward;
lStyle.HorizontalFOA = 70f;
lStyle.VerticalFOA = 40f;
lStyle.DamageModifier = 1f;
lStyle.ActionStyleAlias = "Jump";
lStyle.IsInterruptible = true;
AttackStyles.Add(lStyle);
Now i wish to find the field ParameterID where the ActionStyleAlias is equal to a value (for instance "Jump")
This is for a Unity application so the search / find needs to be as fast as possible.
var result = AttackStyles.FirstOrDefault(x => x.ActionStyleAlias == "Jump").ParameterID;
The straight solution is:
var pId = AttackStyles.FirstOrDefault(x=> x.ActionStyleAlias == "Jump")?.ParameterID
But if you want to get a better performance, it would be better, to index the most useful property which you want. Therefore, construct a dictionary on the most useful fields to get a better performance in time. For example:
var styles = new Dictionary<string, AttackStyle>();
styles.Add("Jump", new AttackStyle()
{
Name = "Attack Achilles",
ParameterID = 0,
Forward = Vector3.forward,
HorizontalFOA = 70f,
VerticalFOA = 40f,
DamageModifier = 1f,
ActionStyleAlias = "Jump",
IsInterruptible = true
});
Then, find the object by this:
var pId = styles["Jump"].ParamterId;
or if it might be null:
if(styles.Keys.Contains("Jump"))
var pId = styles["Jump"].ParamterId;
var param = AttackStyles.First(x => x.ActionStyleAlias.Equals(value)).ParameterID;
Let's return first ParameterID if there's the required item in the collection; -1 otherwise:
var result = AttackStyles
.Where(item => item.ActionStyleAlias == "Jump")
.Select(item => item.ParameterID)
.DefaultIfEmpty(-1)
.First();
You can try extension methods. Also you should consider null cases:
static class Extensions
{
public static int? FindParameterId(this List<AttackStyle> values, string actionStyleAlias)
{
return values.FirstOrDefault(x => x.ActionStyleAlias == actionStyleAlias)?.ParameterID;
}
}
Then use it:
List<AttackStyle> attackStyles = new List<AttackStyle>();
var parameterId = attackStyles.FindParameterId("Jump");
I have many Object1A, say IEnumerable<Object1A>.
public class Object1A {
public string text;
public datetime date;
public decimal percent;
public Object3 obj;
}
Many of these objects have the same text, date, and percent, but have a different obj. I want to transform the list such that the output will be a IEnumerable<Object1B> where
public class Object1B{
public string text;
public datetime date;
public decimal percent;
public IEnumerable<Object3> objs;
}
My current apporach is a bit clunky, and listed below
IEnumerable<Object1A> a = GetSomeConstruct();
var lookup = a.ToLookup( t => t.text);
var b = new List<Object1b>();
foreach(var group in lookup){
var itemA = group.first();
var itemB = new Object1b(){
text = itemA.text,
date = itemA.date,
percent = itemA.percent
};
itemB.objs = pair.Select(t => t.obj);
b.Add(itemB);
}
Can this approach be refined? It doesn't seem to run to slow, but it seems like it could be better. I'm looking for a more terse approach if possible.
edit: yeah, this was a dumb question, cudos to the downvote....
simple answer
var b_objects = a_objects.GroupBy(t => new {t.Text})
.Select( t => new Object1B
{ Text = t.Key.Text,
Percent = t.First().Percent,
Date = t.First().Date,
Objs = t.Select( o => o.Obj).ToList()
});
Guess you want something like this?
var b = from a in GetSomeConstruct()
group a.obj by new { a.text, a.date, a.percent } into grp
select new Object1B
{
text = grp.Key.text,
date = grp.Key.date,
percent = grp.Key.percent,
objs = grp
};
You can use anonymous types with join and group by. Their GetHashCode and Equals overloads operate on each member.
I've an object that is include property ID with values between 101 and 199. How to order it like 199,101,102 ... 198?
In result I want to put last item to first.
The desired ordering makes no sense (some reasoning would be helpful), but this should do the trick:
int maxID = items.Max(x => x.ID); // If you want the Last item instead of the one
// with the greatest ID, you can use
// items.Last().ID instead.
var strangelyOrderedItems = items
.OrderBy(x => x.ID == maxID ? 0 : 1)
.ThenBy(x => x.ID);
Depending whether you are interested in the largest item in the list, or the last item in the list:
internal sealed class Object : IComparable<Object>
{
private readonly int mID;
public int ID { get { return mID; } }
public Object(int pID) { mID = pID; }
public static implicit operator int(Object pObject) { return pObject.mID; }
public static implicit operator Object(int pInt) { return new Object(pInt); }
public int CompareTo(Object pOther) { return mID - pOther.mID; }
public override string ToString() { return string.Format("{0}", mID); }
}
List<Object> myList = new List<Object> { 1, 2, 6, 5, 4, 3 };
// the last item first
List<Object> last = new List<Object> { myList.Last() };
List<Object> lastFirst =
last.Concat(myList.Except(last).OrderBy(x => x)).ToList();
lastFirst.ForEach(Console.Write);
Console.WriteLine();
// outputs: 312456
// or
// the largest item first
List<Object> max = new List<Object> { myList.Max() };
List<Object> maxFirst =
max.Concat(myList.Except(max).OrderBy(x => x)).ToList();
maxFirst.ForEach(Console.Write);
Console.WriteLine();
// outputs: 612345
Edit: missed the part about you wanting the last item first. You could do it like this :
var objectList = new List<DataObject>();
var lastob = objectList.Last();
objectList.Remove(lastob);
var newList = new List<DataObject>();
newList.Add(lastob);
newList.AddRange(objectList.OrderBy(o => o.Id).ToList());
If you are talking about a normal sorting you could use linq's order by method like this :
objectList = objectList.OrderBy(ob => ob.ID).ToList();
In result I want to put last item to first
first sort the list
List<int> values = new List<int>{100, 56, 89..};
var result = values.OrderBy(x=>x);
add an extension method for swaping an elements in the List<T>
static void Swap<T>(this List<T> list, int index1, int index2)
{
T temp = list[index1];
list[index1] = list[index2];
list[index2] = temp;
}
after use it
result .Swap(0, result.Count -1);
You can acheive this using a single Linq statment.
var ordering = testData
.OrderByDescending(t => t.Id)
.Take(1)
.Union(testData.OrderBy(t => t.Id).Take(testData.Count() - 1));
Order it in reverse direction and take the top 1, then order it the "right way round" and take all but the last and union these together. There are quite a few variants of this approach, but the above should work.
This approach should work for arbitrary lists too, without the need to know the max number.
How about
var orderedItems = items.OrderBy(x => x.Id)
var orderedItemsLastFirst =
orderedItems.Reverse().Take(1).Concat(orderedItems.Skip(1));
This will iterate the list several times so perhaps could be more efficient but doesn't use much code.
If more speed is important you could write a specialised IEnumerable extension that would allow you to sort and return without converting to an intermediate IEnumerable.
var myList = new List<MyObject>();
//initialize the list
var ordered = myList.OrderBy(c => c.Id); //or use OrderByDescending if you want reverse order