Linq query for data aggregation - linq

I have this class
public class Line
{
public string ConnectionsIndex{get;set;}
}
my Linq problem is that I have to aggregate these Lines
var l1 = new Line{ ConnectionsIndex="01,02"};
var l2 = new Line{ ConnectionsIndex="02,03"};
var l3 = new Line{ ConnectionsIndex="01,03"};
into this
var l4 = new Line{ ConnectionsIndex="01,02,03"};
It's possible to do with Linq?
DETAIL:
The thing is more complicate (at least for me) when I add the other items that I have in my collection.
var l5 = new Line (ConnectionsIndex = "02,04");
var l6 = new Line (ConnectionsIndex = "03,06");
because do not exist other lines with the pairs 03,04 , 01,04 , 01,06 and 02,06
I do not know if I have explained it well ...
in practice, imagine you have all the points of a polygon, I want to get a line of all the items from the query by giving a list of connections between all points of each polygon.
(my list contains more than one polygon)
One point should not be included in result if not connected to all others.
This is an example of my list content:
ConnectionsIndex="166,171"
ConnectionsIndex="166,174"
ConnectionsIndex="166,333"
ConnectionsIndex="169,170"
ConnectionsIndex="171,175"
ConnectionsIndex="171,334"
ConnectionsIndex="167,174"
ConnectionsIndex="172,174"
ConnectionsIndex="174,335"
ConnectionsIndex="177,341"
ConnectionsIndex="180,200"
ConnectionsIndex="181,183"
ConnectionsIndex="182,199"
ConnectionsIndex="184,185"
ConnectionsIndex="186,188"
ConnectionsIndex="189,192"
ConnectionsIndex="190,230"
ConnectionsIndex="191,375"
In this List you have for example a triangle between 166, 171 and 334
More detail:
var group = lines.Where(x => x.ConnectionsIndex.Split(',').Contains(line. ConnectionsIndex.Split(',')[0]) ||
x. ConnectionsIndex.Split(',').Contains(line. ConnectionsIndex.Split(',')[1])).ToList();
if (group.Count()==1)
{
straight_lines.Add(line);
}
else
{
//Here I have a "group" with all the lines between point.. I want to get distinc points
}

Something like:
var connections = (from line in lines
from connection in line.Split(',')
select connection).Distinct()
.ToArray();
Line line = new Line { ConnectionsIndex = string.Join(",", connections) };
This doesn't order the connections, but you can easily add that if you need it.
This would all be cleaner if you were happy to have ConnectionsIndex as a collection of strings instead of a single delimited string, of course :)

I used this:
var l4 = new Line{
ConnectionsIndex =
string.Join(",", (lines.SelectMany(x => x.ConnectionsIndex.Split(','))
.Distinct()
.OrderBy(s => s)).ToArray())
};

This is the bad way I have found... and it works!
var l = linee.Distinct(
(a, b) => a.ConnectionsIndex == b.ConnectionsIndex,x=>x.ConnectionsIndex.GetHashCode())
.ToList();
var single_lines = new List<Linea>();
var multiple_lines = new List<Linea>();
foreach (var linea in l)
{
var group = l
.Where(x => x.ConnectionsIndex.Split(',').Contains(linea.ConnectionsIndex.Split(',')[0]) ||
x.ConnectionsIndex.Split(',').Contains(linea.ConnectionsIndex.Split(',')[1])).ToList();
if (group.Count()==1)
{
single_lines.Add(linea);
}
else
{
var indexes = new List<string>();
var dist = group.Select(x => new {Index = x.ConnectionsIndex.Split(',').ToList()}).ToList();
foreach (var linea1 in dist)
{
indexes=indexes.Concat(linea1.Index).ToList();
}
var indexstring = new StringBuilder();
foreach (var s in indexes.Distinct().OrderBy(x=>Convert.ToInt32(x)))
{
indexstring.Append(s).Append(',');
}
indexstring.Remove(indexstring.Length - 1, 1);
multiple_lines.Add(new Linea() {ConnectionsIndex = indexstring.ToString()});
}
}
var multi_distinct=multiple_lines.Distinct(
(a, b) => a.ConnectionsIndex == b.ConnectionsIndex, x => x.ConnectionsIndex.GetHashCode())
.ToList();
linee = single_lines.Concat(multi_distinct).ToList();
If you find or known better solutions, you are welcome!

Just a note that I think what you're asking for is a way to find maximal cliques (a concept from graph theory). This is known to be an NP-Hard problem. I think your version will work sometimes, and hopeflly for those cases you're interested in. But, not for complicated cases where anything may be connected to anything else. Indeed, if you have a lot of nodes, those cases aren't feasible, even with large CPU cycle budgets (regardless of LINQ).

Related

linq compare with previous item

I need to filter a list, by removing all items have the same language code as the item before (the items are orderd by time). This way, I want to detect all border crossings.
Is there a way to do this with one line of linq?
var test = new List<Sample> { new Sample("AT", "test1"),
new Sample("AT", "test2") ,
new Sample("AT", "test3") ,
new Sample("DE", "test4") ,
new Sample("DE", "test5") ,
new Sample("DE", "test6") ,
new Sample("AT", "test7") ,
new Sample("AT", "test8") ,
new Sample("AT", "test9")
};
var borderChanges = new List<Sample>();
var lastCountry = "";
foreach (var sample in test)
{
if (sample.country != lastCountry)
{
lastCountry = sample.country;
borderChanges.Add(sample);
}
}
I think this is what are you looking for:
test.Where((x,idx) => idx == 0 || x.Country != test[idx - 1].Country));
In case someone else is curious to see, how this problem can be solved with Aggregate(), here is the answer:
var borderChanges = test.Take(1).ToList();
test.Aggregate((last, curr) =>
{
if (last.Code != curr.Code)
{
borderChanges.Add(curr);
}
return curr;
});
Aggregate() executes the lambda expression for every element in the list, except the first. So we initialize the list with the first item.
In the lamda we add all items where the current item doesnt equal the aggregated value. In our case the aggredated value is always the previously checked item.
In the foreach iteration, access to neighboring elements of the sequence is impossible. Perhaps your goal can be achieved with the Aggregate extension method for IEnumerable<T>, where you can compare neighboring elements of the sequence.

Split into KeyValuePair instead of Array

I have a string of data:
Key1=Value1,Key2=Value2,KeyN=ValueN
I'm trying to split the string into
List<KeyValuePair<string, string>>
I can easily do this:
List<string[]> values = item.Split( ',' ).Select( p => p.Split( '=' ) ).ToList();
but I just can't figure out the way to get that into the List of KeyValuePair's. The closest I've gotten so far is:
List<KeyValuePair<string, string>> values = item.Split( ',' )
.Select( p => new KeyValuePair<string, string>(){ p.Split( '=' ) } ).ToList();
But that's still a bit off :(
I know I can easily do it with a loop but I'd really like to get it working in Linq as practice makes perfect. I've seen quite a few examples already of similar questions like this one, but I can't seem to join the dots between those questions and mine so please forgive me if I've accidentally posted a duplicate.
Any help would really be appreciated, thanks :)
What you've done so far is good. Then, you have two ways to achieve what you want:
Create a method ToKeyValuePair
public static KeyValuePair<string, string> ToKeyValuePair(string[] array)
{
if (array.Length != 2)
throw new ArgumentException("The array must contain exactly 2 elements.");
return new KeyValuePair<string, string>(array[0], array[1]);
}
var values = (item.Split( ',' )
.Select( p => ToKeyValuePair(p.Split( '=' ))))
.ToList();
Use the LINQ query syntax
If I convert the above line into query syntax:
var values = (from p in item.Split( ',' )
select ToKeyValuePair(p.Split( '=' )))
.ToList();
Not much has changed.
But, thanks to this new syntax, it is quite easy to remove the usage of ToKeyValuePair(...) thanks to the let clause:
var values = (from p in item.Split( ',' )
let splittedP = p.Split( '=' ) // Declares a variable
select new KeyValuePair<string, string>(splittedP[0], splittedP[1]))
.ToList();
Of course, the last line can be written with Extention methods syntax (ie with .Select(p=>...)), but is hard to read:
var values = (item.Split(',')
.Select(p => new { p, splittedP = p.Split('=') })
.Select(p => new KeyValuePair<string, string>(p.splittedP[0], p.splittedP[1])))
.ToList();
I know this is an old question but I stumbled across it on google. I solved the problem using the accepted answer but a shortened it a little. You don't need the new { p, splittedP = p.Split('=') } part, just p.Split('=')
var values = data.Split(',').Select(p=>p.Split('='))
.Select(s => new KeyValuePair<string, string>(s[0], s[1]))
.ToList();
You could also do something like this if your keys are unique:
var values = data.Split(',').Select(p => p.Split('='))
.ToDictionary(k => k[0], v => v[1]);
Which is much shorter and basically gets you a list with with O(1) access.
(This is with .NET 4.5)
use above code. must trim the key.
public static string GetUserInfo(this X509Certificate2 x509,X509Oid oid)
{
try
{
var kvs = x509.Subject.Split(',').Select(x => new KeyValuePair<string, string>(x.Split('=')[0].Trim(), x.Split('=')[1].Trim())).ToList();
string value = kvs.FirstOrDefault(A => A.Key == oid.ToString()).Value;
return value;
}
catch (Exception)
{
}
return "";
}
Add an enum file.
public enum X509Oid
{
O,
E,
L,
C,
S,
G,
SN,
CN,
Street
}

Linq to Sql Query - better solution (optimizing)

The following code works, but it's not a nice code. (low performance)
I have a dictionary with value and key.
First i go trough every webcodes who exist. Then i load all participants in a list (where webcode equals the actual webcode in the foreach). After that i add the data (parameter of the webcode and a count of participants to the dictionary).
Guid compID = Guid.Parse(wID);
ChartModel webcodes = new ChartModel();
webcodes.Title = "Webcodes Statistics";
webcodes.Data = new Dictionary<string, int>();
var webcodesData = db.t_Webcode;
foreach (var w in webcodesData)
{
var wData = db.t_Participant.Where(t => t.FK_Competition == compID && t.Webcode == w.Webcode);
if (wData.Count() != 0)
webcodes.Data.Add(w.Parameter, wData.Count());
}
ViewBag.Webcodes = webcodes;
TIA
You need something along these lines:
webcodes.Data = (from w in db.t_Webcode
join p in db.t_Participant on w.Webcode equals p.Webcode
where p.FK_Competition == compID
group w by w.Parameter into g
select new { g.Key, Count = g.Count() }).ToDictionary();
I can't test it but that is the type of query you need.
This will assume that you have relationships defined in your database and that your LINQ to SQL datacontext are aware of them. If not, you will need to join manually on t_Participants from tWebcode.
This should execute in 1 single SQL query, instead of 1 query per row in tWebcode.
var webcodesAndNoOfParticipants =
from webcode in db.tWebcode
// Define number of participants for this webcode
let numberOfParticipants = webcode.t_Participants.Count(participant => participant.FK_Competition == compID)
where numberOfParticipants > 0
select new {
WebcodeParameter = webcode.Parameter,
NoOfParticipants = numberOfParticipants
};
webcodes.Data = webcodesAndNoOfParticipants.ToDictionary(x => x.WebcodeParameter, x => x.NoOfParticipants);

Row number in LINQ

I have a linq query like this:
var accounts =
from account in context.Accounts
from guranteer in account.Gurantors
where guranteer.GuarantorRegistryId == guranteerRegistryId
select new AccountsReport
{
recordIndex = ?
CreditRegistryId = account.CreditRegistryId,
AccountNumber = account.AccountNo,
}
I want to populate recordIndex with the value of current row number in collection returned by the LINQ. How can I get row number ?
Row number is not supported in linq-to-entities. You must first retrieve records from database without row number and then add row number by linq-to-objects. Something like:
var accounts =
(from account in context.Accounts
from guranteer in account.Gurantors
where guranteer.GuarantorRegistryId == guranteerRegistryId
select new
{
CreditRegistryId = account.CreditRegistryId,
AccountNumber = account.AccountNo,
})
.AsEnumerable() // Moving to linq-to-objects
.Select((r, i) => new AccountReport
{
RecordIndex = i,
CreditRegistryId = r.CreditRegistryId,
AccountNumber = r.AccountNo,
});
LINQ to objects has this builtin for any enumerator:
http://weblogs.asp.net/fmarguerie/archive/2008/11/10/using-the-select-linq-query-operator-with-indexes.aspx
Edit: Although IQueryable supports it too (here and here) it has been mentioned that this does unfortunately not work for LINQ to SQL/Entities.
new []{"aap", "noot", "mies"}
.Select( (element, index) => new { element, index });
Will result in:
{ { element = aap, index = 0 },
{ element = noot, index = 1 },
{ element = mies, index = 2 } }
There are other LINQ Extension methods (like .Where) with the extra index parameter overload
Try using let like this:
int[] ints = new[] { 1, 2, 3, 4, 5 };
int counter = 0;
var result = from i in ints
where i % 2 == 0
let number = ++counter
select new { I = i, Number = number };
foreach (var r in result)
{
Console.WriteLine(r.Number + ": " + r.I);
}
I cannot test it with actual LINQ to SQL or Entity Framework right now. Note that the above code will retain the value of the counter between multiple executions of the query.
If this is not supported with your specific provider you can always foreach (thus forcing the execution of the query) and assign the number manually in code.
Because the query inside the question filters by a single id, I think the answers given wont help out. Ofcourse you can do it all in memory client side, but depending how large the dataset is, and whether network is involved, this could be an issue.
If you need a SQL ROW_NUMBER [..] OVER [..] equivalent, the only way I know is to create a view in your SQL server and query against that.
This Tested and Works:
Amend your code as follows:
int counter = 0;
var accounts =
from account in context.Accounts
from guranteer in account.Gurantors
where guranteer.GuarantorRegistryId == guranteerRegistryId
select new AccountsReport
{
recordIndex = counter++
CreditRegistryId = account.CreditRegistryId,
AccountNumber = account.AccountNo,
}
Hope this helps.. Though its late:)

Reading Text Files with LINQ

I have a file that I want to read into an array.
string[] allLines = File.ReadAllLines(#"path to file");
I know that I can iterate through the array and find each line that contains a pattern and display the line number and the line itself.
My question is:
Is it possible to do the same thing with LINQ?
Well yes - using the Select() overload that takes an index we can do this by projecting to an anonymous type that contains the line itself as well as its line number:
var targetLines = File.ReadAllLines(#"foo.txt")
.Select((x, i) => new { Line = x, LineNumber = i })
.Where( x => x.Line.Contains("pattern"))
.ToList();
foreach (var line in targetLines)
{
Console.WriteLine("{0} : {1}", line.LineNumber, line.Line);
}
Since the console output is a side effect it should be separate from the LINQ query itself.
Using LINQ is possible. However, since you want the line number as well, the code will likely be more readable by iterating yourself:
const string pattern = "foo";
for (int lineNumber = 1; lineNumber <= allLines.Length; lineNumber++)
{
if (allLines[lineNumber-1].Contains(pattern))
{
Console.WriteLine("{0}. {1}", lineNumber, allLines[i]);
}
}
something like this should work
var result = from line in File.ReadAllLines(#"path")
where line.Substring(0,1) == "a" // put your criteria here
select line

Resources