Transforming string of data into an object in c# - c#

So I'm trying to solve this issue, which shouldn't be too hard, but I'm stuck on it for far too long now.
This is the data I'm working with var data = "2,6;2,7;4,14;5,20";
It's a string that shows <modifierGroup>,<modifier>;<modifierGroup>,<modifier>;...
This is the model I eventually want to get my data in:
public class ModifierGroup
{
public int Id { get; set; }
public List<Modifier> Modifiers { get; set; }
}
public class Modifier
{
public int Id { get; set; }
}
Right now I keep thinking I need to get my data in this format, so I can eventually push it into the model:
Key=2
Value=6
Value=7
Key=4
Value=14
Key=5
Value=20
But I could be wrong. I'd love to keep the code short. So I'd rather prevent loops in loops and doing if statements over and over. Best case scenario I get a 1 or 2-liner of code, but if it doesn't work, it doesn't work.

You could just use Split and GroupBy with a projection
var data = "2,6;2,7;4,14;5,20";
var result = data
.Split(";")
.Select(x => x.Split(",")
.Select(int.Parse)
.ToArray())
.GroupBy(x => x[0])
.Select(x => new ModifierGroup()
{
Id = x.Key,
Modifiers = x.Select(y => new Modifier() {Id = y[1]}).ToList()
});

Related

get the distinct element(eg: ID) from student detail where collection of student details are inside another array using C#

//Model
public class StudentDetails
{
public int Contact { get; set; }
public string Summary { get; set; }
public string Address { get; set; }
public long ID { get; set; }
}
//controller
public async Task AddStudDetails([FromBody] StudentDetails[] studentDetails){}
You could use LINQ to do this:
var distinctResults = studentDetails.DistinctBy(student => student.ID).ToArray();
This'll yield an array of all the students where no two students have the same ID.
you'd also have to add a using System.Linq; to your imports :)
Edit
if you have a StudentDetails[][] as an input (array of arrays of StudentDetails) and you still want to use LINQ then this would be one way of doing it
StudentDetails[][] myArrArr = ...;
myArrArr
.Aggregate(new List<StudentDetails>(), (resultList, studentDetails) => resultList
.Concat(studentDetails)
.DistinctBy(s => s.ID)
.ToList());
however using a simple dictionary may be faster and less resource intensive
StudentDetails[][] myArrArr = ...;
Dictionary<long, StudentDetails> distinctStudents = new();
foreach (StudentDetails[] array in myArrArr)
{
foreach (StudentDetails details in array)
{
distinctStudents.TryAdd(details.ID, details);
}
}
var result = distinctStudents.Values.ToArray();
Edit 2
I think I finally understood your question, if you just want to extract a single property of a collection of objects you could just do a simple Select()
var allIds = studentDetails.Select(student => student.ID).ToArray();
var stud = from s in studentDetails select s.ID;
figured out the code I was looking for. Hope this will be helpful to others too.

LINQ, Group by property while keeping other property sort together

So I am not entirely sure how to explain what it is I am trying to do here. I am attempting to take some data (represented by the Excel file screenshot below), and basically sort by Connection2, while keeping similar items in Connection1 together. (Explained a bit in screen shot below)
Here is what I have as of right now:
var wires = RedConductorWires
.OrderBy(x => x.Label)
.ThenBy(x => x.Connection1)
.ThenBy(x => x.Connection2)
.ToList();
Class Object being sorted(Matches Excel Screenshot):
public class CustomExcelFormat
{
public string Label { get; set; }
public string WireSize { get; set; }
public string WireColor { get; set; }
public string WirePartNumber { get; set; }
public string Length { get; set; }
public string Connection1 { get; set; }
public string Connection1Torque { get; set; }
public string Connection1Termination { get; set; }
public string Connection1StripLength { get; set; }
public string Checkbox1 { get; set; }
public string Connection2 { get; set; }
public string Connection2Torque { get; set; }
public string Connection2Termination { get; set; }
public string Connection2StripLength { get; set; }
public string Checkbox2 { get; set; }
}
Screen Shot:
THE PROBLEM:
The issue is if you look at the screen shot the brown "A1:TB7:M1" cells need to be grouped together as well, and the Green "K7:10" need to be grouped together while maintaining their Connection2 sort/group.
In other words, the connection 2 side of those, K8:10 and K8:11 need to stay grouped together.
So obviously my LINQ query is not correct, I believe I need to do some sort of grouping and then sorting but am unsure how to approach it or even ask this question exactly (If someone could put it into words for me). I basically need to group by items in connection 2, while still keeping connection 1 sorted and together.
If someone could point me in the direction of the LINQ expression that could do something like this that would be great!
EDIT
So I used the following query:
var wires = RedConductorWires
.OrderBy(x => x.Label)
.GroupBy(x => new { x.Connection2, x.Connection1 })
.Select(grp => grp.ToList()).SelectMany(i => i).ToList();
and got the grouping correct. Now I just need to get it to sort in some alphabetical manner. See picture below.
Imagine this lines
A - B
C - B
C - D
A - D
you can reorder the lines any way you like and either you would have first column grouped or second column grouped. But you can never have both at the same time
I got the grouping to work correctly with the following query. I decided to keep it sorted on label initially.
var wires = RedConductorWires
.OrderBy(x => x.Label)
.GroupBy(x => new { x.Connection2, x.Connection1 })
.Select(grp => grp.ToList()).SelectMany(i => i).ToList();
Group By the values according to Label, Connection1, Connection2 then sort by these 3 fields and finally the desired output is generated.
var wires = RedConductorWires
.GroupBy(a => new { a.Label,a.Connection2, a.Connection1})
.Join(RedConductorWires,
left=>new { left.Key.Label,left.Key.Connection1,left.Key.Connection2},
right => new { right.Label, right.Connection1, right.Connection2 },
(left,right)=>new {left=left.Key,right = right })
.OrderBy(x => x.left.Label)
.ThenBy(x => x.left.Connection2)
.ThenBy(x => x.left.Connection1)
.ToList();
foreach(var item in wires)
{
Console.WriteLine(item.left.Label + "----" + item.left.Connection1 + "-----" + item.left.Connection2);
}
or
var wires = RedConductorWires
.OrderBy(x => x.Label)
.GroupBy(x => new { x.Connection2, x.Connection1 })
.Select(grp => grp.ToList()).SelectMany(i => i)
.OrderBy(x => x.Connection2)
.ThenBy(x => x.Connection1)
.ToList();
foreach(var item in wires)
{
Console.WriteLine(item.Label + "----" + item.Connection1 + "-----" + item.Connection2);
}

Group IEnumerable into a string

I wonder if someone could spare me a few minutes to give me some advice please?
I've created an IEnumerable list:
public class EmailBlock
{
public int alertCategory { get; set; }
public string alertName { get; set; }
public string alertURL { get; set; }
public string alertSnippet { get; set; } //Need to work out the snippet
}
List<EmailBlock> myEmailData = new List<EmailBlock>();
Which I then loop through some data (Umbraco content - not that that's really relevant!) and add items to the list.
myEmailData.Add(new EmailBlock { alertCategory = category.Id, alertName = alert.GetPropertyValue("pageTitle"), alertURL = alert.NiceUrl });
What ultimately I'd like to do is group the list by the alertCategory and then load each 'group' (another loop occurs later to check what members have subscribed to what alert category) into a variable which I can then use as an email's content.
You could use Linq's GroupBy() to do this:
using System.Linq
...
//Create a type to hold your grouped emails
public class GroupedEmail
{
public int AlertCategory { get; set; }
public IEnumerable<EmailBlock> EmailsInGroup {get; set; }
}
var grouped = myEmailData
.GroupBy(e => e.alertCategory)
.Select(g => new GroupedEmail
{
AlertCategory = g.Key,
EmailsInGroup = g
});
You can select to an anonymous type if required and project your sequence into whatever structure you require.
Linq has a nice group by statement:
var emailGroup = emailList.GroupBy(e => e.alertCategory);
Then you can loop through each grouping and do whatever you want:
foreach(var grouping in emailGroup)
{
//do whatever you want here.
//note grouping will access the list of grouped items, grouping.Key will show the grouped by field
}
Edit:
To retrieve a group after you have grouped them, just use Where for more than one or First for just one:
var group = emailGroup.First(g => g.Key == "name you are looking for");
or
var groups = emailGroup.Where(g => listOfWantedKeys.Contains(g.Key));
this is a lot more efficient than looping through every time you need to find something.

Changing GroupBy keys depending on Class Structure

I have a list of items with multiple columns and would like to group them by some fields depending on a boolean:
I have the following class:
public class Item
{
public string Group { get; set; }
public string Person { get; set; }
public string Currency { get; set; }
public string Country { get; set; }
public string County { get; set; }
public string OtherAdd { get; set; }
public string Income { get; set; }
}
which is part of a List:
var results = items.ToList(); //items is IEnumerable<Item>
if int type = 1, then I want to group by more elements:
results = results
.GroupBy(e => new { e.Group, e.Person, e.Branch, e.Currency, e.Country, e.County, e.OtherAdd})
.Select(g => new Item
{
Group = g.Key.Group,
Person = g.Key.Person,
Currency = g.Key.Currency,
Currency = g.Key.Country,
Currency = g.Key.County,
Currency = g.Key.OtherAdd,
Income = g.Sum(p => double.Parse(p.Income, System.Globalization.CultureInfo.InvariantCulture)).ToString("0.00", System.Globalization.CultureInfo.InvariantCulture)
})
.ToList();
if int type = 2, then I want to group by fewer elements (e.g. because OtherAdd would be an empty String):
results = results
.GroupBy(e => new { e.Group, e.Person, e.Branch, e.Currency})
.Select(g => new Item
{
Group = g.Key.Group,
Person = g.Key.Person,
Currency = g.Key.Currency,
Income = g.Sum(p => double.Parse(p.Income, System.Globalization.CultureInfo.InvariantCulture)).ToString("0.00", System.Globalization.CultureInfo.InvariantCulture)
})
.ToList();
etc.
Is there a way for me to change the GroupBy key depending on my integer type without repeating the code?
Well, you could use the old SQL trick, conditional values:
.GroupBy(e => new { e.Group, Person = (e.Type == 1 ? e.Person : Guid.NewGuid().ToString()), ... }
While this will still include the columns in the group by, all the items will have unique keys, so it doesn't quite matter. Sadly, I don't think there's a way around generating the unique keys, unlike in SQL (where you could just use NULL).
A better way might be to implement your own grouping class, instead of using an anonymous type. You could then use your own equality and hashing semantics, to make sure whether you include all the fields or not. However, that is arguably going to be more work than just having the similar code repeated.
Or, you might want to revise your whole design. It doesn't sound like what you're trying to do makes much sense - it's already quite suspicious that you're using the same type for two different things, and using strings for all the fields doesn't help either. Maybe you could try a different object design?

How to order Nested Collections in Linq and EF

i would like to make a treelistview for my Data.
Tree should look like this
Accounts
-> Providers
-> Accounts
public sealed class AccountRoot
{
public AccountRoot()
{
Providers = new Collection<Hoster>();
}
public long AccountRootId { get; set; }
public ICollection<Hoster> Providers { get; set; }
}
public sealed class Hoster
{
public Hoster()
{
Accounts = new Collection<Account>();
}
[Key]
public long HosterId { get; set; }
public long AccountRootId { get; set; }
public string Name { get; set; }
public ICollection<Account> Accounts { get; set; }
}
public sealed class Account
{
[Key]
public long AccountId { get; set; }
public long HosterId { get; set; }
public Hoster Hoster { get; set; }
public string Name { get; set; }
}
I would like to order my query.
should be sth like
Accounts
Providers A-Z
Accounts A-Z
what i got until now is..
var query = _entity.AccountRoot.Local
.Select(x => new AccountRoot()
{
AccountRootId = x.AccountRootId,
Providers = x.Providers.OrderBy(y => y.Name).ToList()
}).ToList();
What is missing is the orderby for the next nested collection.
Thank you for your help ! :-)
It can be a bit different approaches depending on if you already have a result set, and want to just sort it in code, or if you want to construct IQueryable<> for EF which will be successfully compiled to SQL and executed with actual sorting in database.
First, assume you already have the collection in code. In this case, you have object AccountRoot, which contains collection of Providers, each of which has collection of Accounts. Obviously, you cannot return the same objects, as you need to reorder collection properties, so all you need is to just construct new ones. I would just sort the collections, but you could construct completely new entities, if you need:
var query = ...
.Select(x => new AccountRoot
{
// add copy properties here
// ....
Providers = x.Providers
.Select(y =>
{
// Here we can construct completely new entity,
// with copying all properties one by one,
// or just reorder existing collection as I do here
var result = y;
result.Accounts = y.Accounts.OrderBy(z => z.Name).ToArray();
return result;
})
.OrderBy(y => y.Name)
.ToArray()
})
.ToArray();
Second case, if you need to get it directly from SQL, is a bit different, as you cannot use all that var result = ...; ... return result stuff in lambda - it won't compile to SQL. But idea is the same - you need to construct projection from data sets. It should be something like this:
var query = ...
.Select(x => new AccountRoot
{
AccountRootId = x.AccountRootId,
// Other properties to copy
// ...
Providers = x.Providers
.Select(y => new Hoster
{
HosterId = y.HosterId,
// Other properties to copy
// ...
Accounts = y.Accounts.OrderBy(z => z.Name).ToArray(),
})
.OrderBy(y => y.Name)
.ToArray()
})
.ToArray();

Categories