Use list<int> as a linq group by key - c#

This is from LinqPad (hence the .Dump())
void Main()
{
var k1 = new List<int>(){1,2,3,4,5};
var k2 = new List<int>(){1,2,3,4,5};
var o1 = new A(){k=k1, objectName="o1"};
var o2 = new A(){k=k2, objectName="o2"};
var l = new List<A>();
l.Add(o1);
l.Add(o2);
var b = from a in l
group a.objectName by a.k into g
select new {g.Key, n = String.Join(",",g)};
b.Dump();
}
public class A {
public List<int> k;
public string objectName;
}
The problem is that it doesn't work, the code above yields:
I know why this is happening, it's because the list objects are separate objects, but what I was wondering is if there is a way to tell group by to use content of the list rather than the object instance.

public static void Main()
{
var k1 = new List<int>(){1,2,3,4,5};
var k2 = new List<int>(){1,2,3,4,5};
var o1 = new A(){k=k1, objectName="o1"};
var o2 = new A(){k=k2, objectName="o2"};
var l = new List<A>();
l.Add(o1);
l.Add(o2);
// Use custom comparer for the list
var b = l.GroupBy(a => a.k, new ListComparer<int>())
.Select(g => new
{
Key = String.Join(",", g.Key.Select(i => i)),
n = String.Join(",",g.Select(i => i.objectName))
});
foreach(var item in b)
{
Console.WriteLine(string.Format("{0} : {1}", item.Key, item.n));
// 1,2,3,4,5 : o1,o2
}
}
public class A
{
public List<int> k;
public string objectName;
}
public class ListComparer<T> : IEqualityComparer<List<T>>
{
// Ignore the order and compare with the sequence value for the equality
public bool Equals(List<T> left, List<T> right)
{
return left.OrderBy(i => i).SequenceEqual(right.OrderBy(i => i));
}
public int GetHashCode(List<T> list)
{
return list.Count;
}
}
.Net Fiddle

You can convert array to string.
var b = l.GroupBy(o => string.Join(";", o.k))
.Select(g => new { Key = g.First().k,
n = string.Join(",", g.Select(o => o.objectName)) });

I managed to accomplish the following:
var b = l.SelectMany(a => a.k.Select(i => new { Key = i, n = a.objectName }))
.GroupBy(i => i.Key);

Related

ASP.NET Order bool with not set

How do I order a bool by null first, then true, then false
return View("Index", db.HolidayRequestForms.ToList().OrderByDescending(e => e.Approved).ThenBy(e => e.RequestID))
I am using a using a custom display template for the bool, I don't know if that matters
You could use a custom comparer
public class ApprovedComparer : IComparer<bool?>
{
public int Compare(bool? x, bool? y)
{
var a = 0;
var b = 0;
if (x.HasValue)
a = x.Value ? 1 : 2;
if (y.HasValue)
b = y.Value ? 1 : 2;
return a - b;
}
}
Usage:
return View("Index", db.HolidayRequestForms.ToList()
.OrderBy(e => e.Approved, new ApprovedComparer())
.ThenBy(e => e.RequestID))
Can be tested in LinqPad (or a normal console app)
public class Thing
{
public string Name { get; set; }
public bool? Approved { get; set; }
}
public class ApprovedComparer : IComparer<bool?>
{
public int Compare(bool? x, bool? y)
{
var a = 0;
var b = 0;
if (x.HasValue)
a = x.Value ? 1 : 2;
if (y.HasValue)
b = y.Value ? 1 : 2;
return a - b;
}
}
void Main()
{
var thing1 = new Thing { Approved = null, Name = "Thing 1" };
var thing2 = new Thing { Approved = true, Name = "Thing 2", };
var thing3 = new Thing { Approved = false, Name = "Thing 3" };
//note the 'incorrect' order
var listOfThings = new[] { thing3, thing2, thing1 };
listOfThings
.OrderBy(x => x.Approved, new ApprovedComparer())
.Select(x => x.Name) //just for outputting the names
.Dump(); //LinqPad specifc
}
Output
As of .net 4.5, you can use Comparer<T>.Create() to create a static comparer, which can be 'inline' - ie, no separate class required.
Personally, I find the separate class a bit cleaner to read. Just my opinion, however.
var comparer = Comparer<bool?>.Create((x, y) =>
{
var a = 0;
var b = 0;
if (x.HasValue)
a = x.Value ? 1 : 2;
if (y.HasValue)
b = y.Value ? 1 : 2;
return a - b;
});
listOfThings
.OrderBy(x => x.Approved, comparer)
.Select(x => x.Name) //just for outputting the names
.Dump(); //LinqPad specifc
You can use this:
myList.OrderBy(v => !v)

Partition graph by connection degree

I would to ask if there is already algorithms for graphs that partition graphs into subgraphs like the screenshot attached:
Graph has edges
A-B,B-C,C-D, D-E, C-F, F-G
I need to partition it to 3 parts since vertex C has degree of 3:
A-B-C
C-D-E
C-F-G
First I was thinking that I can remove C node and disconnect graph using typical methods. But maybe there already known method to partition graphs by nodes degree?
I wrote a simple algorithm for this. Please note that the graph is needed to be ordered
public static void Main()
{
Console.WriteLine("Hello World");
var str = "A-B,B-C,C-D,D-E,C-F,F-G";
var resultSet = Graph(str.Split(','), '-');
}
public static string[] Graph(string[] s, char delimiter)
{
var resultSet = new List<string>();
var prevOperLeft = "";
var prevOperRight = "";
foreach (var part in s)
{
var oper = part.Split(delimiter);
var left = oper[0];
var right = oper[1];
if (prevOperRight == left)
{
resultSet.Add(string.Format("{0}{1}{2}{3}{4}", prevOperLeft, delimiter, left, delimiter, right));
prevOperLeft = prevOperRight = "";
}
else
{
prevOperLeft = left;
prevOperRight = right;
}
}
return resultSet.ToArray();
}
https://dotnetfiddle.net/e3kmpR
More generic example with LinkedList
public static IList<LinkedList<T>> Graph2<T>(LinkedList<T> ll) where T: class
{
var resultSet = new List<LinkedList<T>>();
T prevOperLeft = null;
T prevOperRight = null;
while (ll.Count > 0)
{
var left = ll.First.Value;
ll.RemoveFirst();
var right = ll.First.Value;
ll.RemoveFirst();
if (prevOperRight != null && prevOperRight.Equals(left))
{
resultSet.Add(new LinkedList<T>(new []{ prevOperLeft, left, right }));
prevOperLeft = prevOperRight = null;
}
else
{
prevOperLeft = left;
prevOperRight = right;
}
}
return resultSet;
}
public static void Main()
{
var A = new MyClass {Name = "A"};
var B = new MyClass {Name = "B"};
var C = new MyClass {Name = "C"};
var D = new MyClass {Name = "D"};
var E = new MyClass {Name = "E"};
var F = new MyClass {Name = "F"};
var G = new MyClass {Name = "G"};
List<MyClass> list = new List<MyClass>
{
A,B,B,C,C,D,D,E,C,F,F,G
};
LinkedList<MyClass> ll = new LinkedList<MyClass>(list);
var resultSet2 = Graph2(ll);
}
class MyClass
{
public string Name { get; set; }
}

Get result from list inside list

I have a structure like
class a
{
public IList<b> bs{ get; set; }
public class b
{
public string r{ get; set; }
public IList<sl> sls{ get; set; }
public class sl
{
public string sn{ get; set; }
public string st{ get; set; }
}
}
}
the query is like if sn == "abc" then get r
I have done
a aobj = new a();
var aa = aobj.bs.Where(c => c.sl != null).Select(c => c).ToList(); // here I get `r = "qwerty", sls will have data like sn = "qwerty0", st= "1" ; sn = "asdf" , st="2"; sn = "zxc" st = "abc"; sn="me" , st = "abc"
var bb = aa.where(c => c.sl.Select(dr => dr.st.ToLower().Contains("abc"))); // I 'm here checking that `sn` contain abc or not
var cc = bb.Select(c => c.r).ToList(); // result
my expected output of query is "zxc", "me"
but I am getting all the list not only contains abc.. can anyone suggest me what should I do? I am partitioning this query to debug.
Thank you
You'll need to use the Any operator to check if an enumerable collection has an item that meets a criteria.
You can't use Select as that only projects an item, it isn't returning an predicate and as such has no function in a where clause.
Here is your (fixed for syntax errors) changed code:
var aa = aobj.bs.Where(c => c.sls != null).Select(c => c).ToList();
// use Any here
var bb = aa.Where(c => c.sls.Any(dr => dr.sn.ToLower().Contains("abc")));
var cc = bb.Select(c => c.r).ToList();
And here is the test set I used:
a aobj = new a();
aobj.bs = new List<b>();
aobj.bs.Add(new b {
r ="bar",
sls = new List<sl>{
new sl { sn="tets"},
new sl { sn="no"}
}
});
aobj.bs.Add(new b {
r ="foo",
sls = new List<sl>{
new sl { sn="no"},
new sl { sn="abc"}
}
});
aobj.bs.Add(new b {
r ="fubar",
sls = new List<sl>{
new sl { sn="no"},
new sl { sn="abc"}
}
});
This will output:
foo
fubar
If you combine all operators together you'll get:
var merged = aobj
.bs
.Where(c => c.sls != null
&& c.sls.Any(dr => dr.sn.ToLower().Contains("abc")))
.Select(c => c.r);
I think you can use a code like this:
var cc = a.bs
.Where(w => w.sls?.Any(s => s.st?.ToLower().Contains("abc") ?? false) ?? false)
.Select(c => c.r);

String split to object with Linq?

Given:
class T
{
public string A { get; set; }
public string B { get; set; }
}
string s = "A|B";
Is there a way to split s on the | and return a T object "inline"? I know I can do something like:
s.Select(x => { string[] arr = s.Split(); return new T() { A = arr[0], B = arr[1] };
But I'm wondering if there is some obscure linq thing to do it "inline" without declaring the array and splitting inside of the select. Something more along the lines of:
s.Split().Select(x => new T() { A = x[0], B = x[1] });
Obviously that'll give you a compiler error, but you get the idea... is there a way to do it like that?
If you want to do it in one line, sure:
var s = "A|B";
var t = new T{ A = s.Split('|')[0], B = s.Split('|')[1] };
But obviously that uses Split twice and it looks bad.
This is probably a sign that you need a method:
private static T ParseT(string s) {
// do the conversion *properly* here
}
Then you can just call it:
ParseT("A|B")
Alternatively, add an explicit (recommended) or implicit conversion:
public static explicit operator T(string s) {
// do the conversion *properly* here
}
If you use query syntax then you could do something like this:
var strings=new string[] { "A|B","C|D"};
var query= from s in strings
let x=s.Split('|')
select new T{ A = x[0], B = x[1] };
Update
If "A|B" is the data source I don't recommend use Linq for that, you just could do:
var arr= str.Split('|');
var instance=new T{A = arr[0],B=arr[1]};
Or do the same in the constructor as #James recommended in his answer.
It's not really better than what you originally had, but this is one way (if s is a IEnumerable<string>:
s.Select(x=>x.Split('|')).Select(x=>new T{A=x[0],B=x[1]});
if s is a single string, then you would do:
new List<string>{s} // Now List<string> with 1 string in the list
.Select(x=>x.Split('|')) // now IEnumerable<string[]> with 1 string array in it
.Select(x=>new T{A=x[0],B=x[1]}) // now IEnumerable<T> with 1 T in it
.First(); // Now just T
Why fight actual language concepts:
void Main()
{
var strs = new List<string> { "A|B", "CCC|DD", "E|FFF"};
var Ts = strs.Select(s =>s.ToT() );
Ts.Dump();
}
static class Ext
{
static public T ToT(this string str)
{
return new T(str);
}
}
public class T
{
public string A { get; set; }
public string B { get; set; }
public T(string str)
{
var arr= str.Split('|');
A = arr[0];
B = arr[1];
}
}
NOTE I do not recommend doing it this way this was more of a fun way to do.
public static T CreateLinq(string s)
{
return s.Aggregate((a: new StringBuilder(), b: new StringBuilder(), c: false),
(acc, c) => (a: (!acc.c && c != '|' ? acc.a.Append(c) : acc.a),
b: (acc.c && c != '|' ? acc.b.Append(c) : acc.b),
c: acc.c || c == '|'),
acc => new T { A = acc.a.ToString(), B = acc.b.ToString() });
}
And the performance is not as bad as it seams on the first glance.
public class T
{
public string A { get; set; }
public string B { get; set; }
};
static void Main(string[] args)
{
var s1 = Enumerable.Range(0, 1000000).Aggregate(new StringBuilder(), (acc, i) => acc.Append("A")).ToString();
var s2 = Enumerable.Range(0, 1000000).Aggregate(new StringBuilder(), (acc, i) => acc.Append("B")).ToString();
var text =$"{s1}|{s2}";
for (int i = 0; i < 5; i++)
{
Stopwatch sw = new Stopwatch();
Console.WriteLine("Start");
sw.Start();
var t1 = CreateT(text);
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
var t2 = CreateLinq(text);
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
sw.Reset();
}
Console.ReadLine();
}
public static T CreateLinq(string s)
{
return s.Aggregate((a: new StringBuilder(), b: new StringBuilder(), c: false),
(acc, c) => (a: (!acc.c && c != '|' ? acc.a.Append(c) : acc.a),
b: (acc.c && c != '|' ? acc.b.Append(c) : acc.b),
c: acc.c || c == '|'),
acc => new T { A = acc.a.ToString(), B = acc.b.ToString() });
}
public static T CreateT(string s)
{
var split = s.Split('|');
return new T { A = split[0], B = split[1] };
}

How to break an object into chunks based on some property?

public class InvestorMailing
{
public string To { get; set; }
public IEnumerable<string> Attachments { get; set; }
public int AttachmentCount { get; set; }
public long AttachmentSize { get; set; }
}
i have an IList<InvestorMailing> mailingList. if the attachment size is greater than x, then i need to split my object into chunks. is there an easy linq-y way to do this?
Edited:
this is how i'm generating my mailings:
var groupedMailings = mailingList.GroupBy(g => g.GroupBy);
var investorMailings = groupedMailings.Select(
g => new DistinctInvestorMailing
{
Id = g.Select(x => x.Id).FirstOrDefault(),
To = g.Key.Trim(),
From = g.Select(x => x.From).FirstOrDefault(),
FromName = g.Select(x => x.FromName).FirstOrDefault(),
Bcc = g.Select(x => x.Bcc).FirstOrDefault(),
DeliveryCode = g.Select(x => x.DeliveryCode).FirstOrDefault(),
Subject = g.Select(x => x.Subject).FirstOrDefault(),
Body = g.Select(x => x.Body).FirstOrDefault(),
CommentsOnStatus = g.Select(x => x.CommentsOnStatus).FirstOrDefault(),
Attachments = g.Select(x => x.AttachmentPath),
AttachmentCount = g.Select(x => x.AttachmentPath).Count(),
AttachmentSize = g.Sum(x => x.AttachmentSize),
MailType = g.Select(x => x.MessageType).FirstOrDefault()
}
).ToList();
It should be pretty simple to do it with a standard method. Consider this example:
class Foo
{
public Foo(int weight) { Weight = weight; }
public int Weight { get; set; }
}
...
IEnumerable<IList<Foo>> GroupFoosByWeight(IList<Foo> foos, int weightLimit)
{
List<Foo> list = new List<Foo>();
int sumOfWeight = 0;
foreach (Foo foo in foos)
{
if (sumOfWeight + foo.Weight > weightLimit)
{
yield return list;
sumOfWeight = 0;
list.Clear();
}
list.Add(foo);
sumOfWeight += foo.Weight;
}
if (list.Count > 0)
yield return list;
}
...
List<Foo> foos = new List<Foo>()
{
new Foo(15), new Foo(32), new Foo(14), new Foo(19), new Foo(27)
};
foreach (IList<Foo> list in GroupFoosByWeight(foos, 35))
{
Console.WriteLine("{0}\t{1}", list.Count, list.Sum(f => f.Weight));
}
Edit
I worked on it a bit and produced a LINQ version. It doesn't really save much code in this case, but it's a start.
int weightLimit = 35;
int fooGroup = 0;
int totalWeight = 0;
Func<Foo, int> groupIncrementer = f =>
{
if (totalWeight + f.Weight > weightLimit)
{
fooGroup++;
totalWeight = 0;
}
totalWeight += f.Weight;
return fooGroup;
};
var query = from foo in foos
group foo by new { Group = groupIncrementer(foo) }
into g
select g.AsEnumerable();
foreach (IList<Foo> list in query)
{
Console.WriteLine("{0}\t{1}", list.Count, list.Sum(f => f.Weight));
}
Here's a way to do it using some LINQ to find a chunk that has enough space left to add the attachment:
var chunks = new List<List<InvestorMailing>>();
int maxAttachmentsSize = 10;
foreach (InvestorMailing mail in mailingList)
{
var chunkWithSpace = chunks
.Where(list => list.Sum(x => x.AttachmentSize) +
mail.AttachmentSize <= maxAttachmentsSize)
.FirstOrDefault();
if (chunkWithSpace != null)
{
chunkWithSpace.Add(mail);
} else {
chunks.Add(new List<InvestorMailing> { mail });
}
}
The result is stored in chunks.
Yes:
var highs = mailingList.Where(i => i.AttachmentSize > 10000).ToList();
var lows = mailingList.Where(i => i.AttachmentSize <= 10000).ToList();
How do you need to break them apart aside from this?
HTH.

Categories