I'm using the following pattern in C#:
IList<foo> x = y.Select(a => new foo
{
b = Calc1(),
c = Calc2()
}).ToList();
foreach(foo f in x)
{
f.d = b / c;
}
What I would like to do though is:
IList<foo> x = y.Select(a => new foo
{
b = Calc1(),
c = Calc2()
d = b / c;
}).ToList();
So the question is: How can you modify this pattern to allow the assignment of a value that is dependent on other values being calculated during the assignment?
(Somebody will probably point out that d should be a property that does the calculation and return a value. This is a contrived example. Assume that the value of d is calculated using other values in addition to c & b which are not available later.)
If you expand this to use the full LINQ syntax:
IList<foo> x = (from a in y
let bq = Calc1()
let cq = Calc2()
select new foo {
b = bq,
c = cq,
d = bq / cq
}).ToList();
This will get you what you want.
There was an answer recommending you repeat your method calls (ie, d = Calc1() / Calc2()) - but I would recommend against this, considering it may be possible that Calc1() and Calc2() are expensive operations, and needlessly performing them twice may have performance implications.
You can't re-use initialized properties in an initializer.
I like Erik's technique. If the query expression syntax is a bother, you can use a full-on anonymous method.
List<int> y = new List<int>() { 1, 2, 3, 4 };
var x = y.Select(a =>
{
int b = a + 1;
int c = a + 2;
int d = b / c;
return new { b = b, c = c, d = d };
});
Related
I have a txt file of over a 100k rows with values like this.
A B C D 1 2
A B C E 1 3
D E C F 1 3
D E C F 1 3
A B C B 1 2
E F G G 1 1
I read the file and fill an object with it and then add it into a list but what I need to do next is take the values that have certain properties the same summing one number column which always has a value 1 for those rows with those repeating values. So in the example i would get a list with objects like so
A B C 3 6
D E C 2 6
E F G 1 1
There are 3 A B C values so I leave only one and the number is really the sum but also the count since that value is always 1. The other columns are different but irrelevant to me if the 3 I look at are the same then I consider the object to be the same.One way I have found to do the grouping is using LINQ and group by with a key and if I also create a counter I also get a count of every value (which is the sum since the number is always 1) however this does not give me what I need.
Is there any way using LINQ after the group by to get this effect? Or another method?
EDIT
My Latest attempt
var dupes = serijskaLista.GroupBy(e => new { e.sreIsplatio, e.sreIznDob, e.sreSif, e.sreSerija })
.Select(y => new { Element = y.Key, Counter = y.Count()});
ConcurrentBag<SreckaIsplacena> sgmooreList = new ConcurrentBag<SreckaIsplacena>();
List<Srecka> _srecke = _glavniRepository.UcitajSamoaktivneSrecke().OrderByDescending(item => item.ID).ToList<Srecka>();
ConcurrentBag<SreckaIsplacena> pomList = new ConcurrentBag<SreckaIsplacena>();
SreckaIsplacena _isplacena;
SreckaNagrade nag;
Parallel.ForEach(dupes, new ParallelOptions { MaxDegreeOfParallelism = 10 }, (dp) =>
{
Srecka srec = (from s in _srecke
where s.Sifra == dp.Element.sreSif && s.Serija == dp.Element.sreSerija
select s).First();
ConcurrentBag<SreckaNagrade> sreckaNagrade = new ConcurrentBag<SreckaNagrade>(_glavniRepository.DohvatiNagradeZaSrecku(srec.ID));
if (sreckaNagrade != null)
{
nag = (from sn in sreckaNagrade
where sn.Iznos == dp.Element.sreIznDob
select sn).FirstOrDefault();
Odobrenje odo = new Odobrenje();
odo = odo.DohvatiOdobrenje(valutaGlavna.ID, dp.Element.sreIsplatio).FirstOrDefault();
if (odo != null)
{
ConcurrentBag<PorezSrecka> listaPoreza = new ConcurrentBag<PorezSrecka>(_glavniRepository.UcitajPorezSrecka(valutaGlavna, odo, srec, nag.NagradaId));
_isplacena = new SreckaIsplacena();
decimal iz = Convert.ToDecimal(dp.Element.sreIznDob);
_isplacena.BrojDobitaka = dp.Counter;
_isplacena.Iznos = iz;
_isplacena.Nagrada = nag;
_isplacena.Prodavac = dp.Element.sreIsplatio;
_isplacena.Valuta = valutaGlavna;
_isplacena.Srecka = srec;
_isplacena.Cijena = Convert.ToDecimal(srec.Cijena);
if (listaPoreza.Count == 1)
{
PorezSrecka ps = listaPoreza.ElementAt(0);
_isplacena.SreckaPorez = ps;
}
lock (_isplacena)
{
_isplacena.Save();
lock (pomList)
{
pomList.Add(_isplacena);
}
}
}
}
});
What happens is this seems to insert correctly into the DB but the ConcurrentBag is not filled correctly. I don't understand why
This does not give you the results you desire, as I don't know how the last number is calculated, and you say the actual calculation of the last number is outside the scope the question.
So, as an example, I will use a calculation of doubling the maximum of the last number in the input group. You will need to replace that with the actual calculation.
e.g.
string text= #"
A B C D 1 2
A B C E 1 3
D E C F 1 3
D E C F 1 3
A B C B 1 2
E F G G 1 1
";
// Split into lines and then by spaces to get data which can be queried.
var data = text.Split(new char[] { '\r', '\n'} , StringSplitOptions.RemoveEmptyEntries)
.Select(l=>l.Split(new char[] { ' '}))
.Select(a => new
{
L1 = a[0], L2 = a[1], L3 = a[2], L4 = a[3],
N1 = Convert.ToInt32(a[4]),
N2 = Convert.ToInt32(a[5])
}
);
// Group by the first three letters
// and calculate the numeric values for each group
var grouped = (from r in data group r by r.L1 + " "+ r.L2 + " " + r.L3 into results
select new
{
results.Key,
N1 = results.Sum(a=>a.N1) , // or N1 = results.Count() ,
N2 = results.Max(a=>a.N2) * 2 // Replace with actual calculation
}
);
grouped.Dump();
// Or if you want it export back to text
var text2 = String.Join("\r\n", grouped.Select(a => $"{a.Key} {a.N1} {a.N2}"));
text2.Dump();
Results in LinqPad would be
I have the following relation (for example)
A contains one or more B's
Each B contains one or more C's and D's
I want to flatten everything using SelectMany along with some search conditions and get A,B,C and D's . This is what i have.
context.A.Where(a => (string.IsNullOrEmpty(name) || a.Name.Contains(name)))
.SelectMany(ab =>ab.b.Where(n=>n.bname.Contains(name) || string.IsNullOrEmpty(name)),
(aa, bb) => new { aa, bb }) //gets all a's and b's
.SelectMany(bc => bb.c.Where(w => w.KEYWORD.Contains(Keyword) || string.IsNullOrEmpty(Keyword)),
(bc,words) => new {bc,kwords}) //gets all b's and c's
Is what i am doing right? If so , then how to get B along with all D's adding to the above expression?
Data Selection using Lambda Syntax:
var flatData = context.A.SelectMany(a => a.B.SelectMany(b => b.Select(new {a,b,c = b.C,d = b.D})
Going further, following checks shall be done before applying the Where Clause, as they check the constant input supplied, name and keyword
string.IsNullOrEmpty(name)
string.IsNullOrEmpty(keyword)
Remaining checks would be simple:
if(!string.IsNullOrEmpty(name))
flatData = flatData.Where(data => data.a.Name.Contains(name))
.Where(data => data.b.Name.Contains(name));
if(!string.IsNullOrEmpty(keyword))
flatData = flatData.Where(data => data.c.Keyword.Contains(keyword));
Important points:
flatData above has a cascading filter, first on a.Name, b.Name and c.Keyword
Agreeing with what Ivan suggested you can flatten this 3 levels deep structure like this:
var query = (from a in A
from b in (List<dynamic>)a.b
from c in (List<dynamic>)b.c
from d in (List<dynamic>)b.d
select new { a, b, c, d });
if (!string.IsNullOrEmpty(name))
{
query = query.Where(record => record.b.bname.Contains(name));
}
if (!string.IsNullOrEmpty(keyword))
{
query = query.Where(record => record.c.keyword.Contains(keyword));
}
var result = query.ToList();
You can also add the where clauses in the query at the top but seeing that you are checking if you got any valid input at all I'd put it after
Tested it with this sample data:
List<dynamic> A = new List<dynamic>
{
new { b = new List<dynamic> { new { bname = "a", c = new List<dynamic> { new { keyword = "b" } }, d = new List<dynamic> { 1, 2, 3 } } } },
new { b = new List<dynamic> { new { bname = "a", c = new List<dynamic> { new { keyword = "d" } }, d = new List<dynamic> { 1, 2, 3 } } } }
};
string name = "a";
string keyword = "b";
I want to compare two non related entities by using linq.
e.g -
Entity A
Id Name
1 A
2 B
3 C
4 D
Entity B
Id Name
1 B
2 C
Result I should get
A, D
From the above two collection I want to compare Entity B with Entity A by using the Name property and find out the records which are not available in Entity B.
Any help will be appreciated with some sample code.
Regards
You can use the Except extension method of LINQ. Quote from MSDN Documentation...
Produces the set difference of two sequences by using the default equality comparer to compare values.
Sample Code
int[] a = { 1, 2, 3, 4, 5 };
int[] b = { 4,5,6,7 };
var c = a.Except(b);
Result
1,2,3
Note
Because you are working with a custom object (a class) you will need to implement an equality comparer that compares items by the Name property. Example of custom equality comparer...
public class CustomComparer : IEqualityComparer<CustomObject>
{
public bool Equals(CustomObject x, CustomObject y)
{
return x.Name.Equals(y);
}
public int GetHashCode(CustomObject obj)
{
return obj.Name.GetHashCode();
}
}
Then you can use this custom equality comparer in an overload of the Except extension method, assumming a and b are of type CustomObject...
var c = a.Except(b, new CustomComparer());
The advantage is re-usability, especially if you are spreading this call to Except all over your project. Then,if you need to change your entity(custom object) you only have make changes in the custom equality comparer
var result = entityAs.Where(a => !entityBs.Any(b => b.Name == a.Name)).ToList();
Create a Class
class MyClass
{
public int Id {get; set;}
public string Name {get; set;}
}
Create List1
List<MyCLass> li1 = new List<MyCLass>();
MyCLass o1 = new MyClass();
o1.Id = 1;
o1.Name = "A";
li1.Add(o1);
o1 = new
o1.Id = 2;
o1.Name = "B";
li1.Add(o1);
o1 = new new MyClass();
o1.Id = 3;
o1.Name = "C";
li1.Add(o1);
o1 = new new MyClass();
o1.Id = 4;
o1.Name = "D";
li1.Add(o1);
Create List2
List<MyCLass> li2 = new List<MyCLass>();
o1 = new new MyClass();
o1.Id = 1;
o1.Name = "B";
li2.Add(o1);
o1 = new new MyClass();
o1.Id = 2;
o1.Name = "C";
li2.Add(o1);
o1 = new new MyClass();
o1.Id = 3;
o1.Name = "D";
li2.Add(o1);
Select only Selected items which you want to compare from List1
List<string> SelectedName = li1.Select(s => s.Name).ToList();
FinalList1 only Get those Item which are in List2
var FinalList = li2.Where(w => SelectedName.Contains(w.Name)).ToList();
/// or
FinalList2 only Get those Item which are not in List2
var FinalList2 = li2.Where(w => !SelectedName.Contains(w.Name)).ToList();
I have a normal GroupBy operation on an enumerable:
e.GroupBy(i => i.Property)
But if i.Property is really a collection, how would I break apart the collection and use the list's elements as grouping keys?
For example let's say I have two objects (Z, Y) that each have a list:
Z: { List = { A, B, C }}
Y: { List = { B, C, D }}
Now running the GroupBySubelement(o => o.List) would not group by the list itself, but would iterate over the list and generate the following Groupings.
{A, {Z}}
{B, {Z, Y}}
{C, {Z, Y}}
{D, {Y}
Is this possible?
Thanks!
Here's some example code that achieves what you want:
//This is just temporary data. Has the similar structure to what you want.
var parts = new[]
{
new
{
Name = "X",
Property = new[] {'A', 'B', 'C'}
},
new
{
Name = "Y",
Property = new[] {'B', 'C', 'D'}
},
new
{
Name = "Z",
Property = new char[] { }
}
};
var groupedBySub = from part in parts
from sub in part.Property
group part by sub;
foreach(var group in groupedBySub)
{
Console.WriteLine("{0} - {1}", group.Key, string.Join(", ", group.Select(x => x.Name)));
}
Which outputs:
A - X
B - X, Y
C - X, Y
D - Y
You can also achieve this in the method chain fashion:
var groupedBySub = parts.SelectMany(part => part.Property, (part, sub) => new {part, sub}).GroupBy(t => t.sub, t => t.part);
If you want to capture it with the list being empty:
var groupedBySub = from part in parts
from sub in part.Property.DefaultIfEmpty()
group part by sub;
Which when substituted for the code above, gives output:
A - X
B - X, Y
C - X, Y
D - Y
- Z
This would do:
var combinations = e.SelectMany(i => i.List.Select(x => new { x, i }));
var groups = combinations.GroupBy(c => c.x, c => c.i);
Part of the problem here is that you don't have a good data structure:
var z = new List<T>(); // I'm using T here, so let's pretend this is in a generic method
var y = new List<T>();
// add a bunch of stuff
There isn't really any algorithm that can get you what you want, because the variables Z and Y are not really known to the data structure, just the comiler.
But what if you had a data structure like this:
var allOfTheLists = new Dictionary<T, List<T>>();
You could then break it out using something like this:
var explodedList = allOfTheLists.SelectMany((pair) => pair.Value.Select((item) => new { pair.Key, item}));
var grouping = explodedList.GroupBy((explodedItem) => explodedItem.item);
Class sam
{
public void m1()
{
List<int> A = new List<int>() {1,2,3};
List<int> B = new List<int>() {4,5,6};
for (int i = 0; i < A.count; i++)
{
c.add(m2(A[i], B[i]));
}
}
public int M2(int a, int b)
{
return a + b;
}
}
In this program i retrive from two lists and pass aruguments. I like call the method m2 in linQ
It's unclear from the question, but perhaps you mean:
var C = A.Zip(B, (a, b) => a + b)
.ToList();
If you want to hand over the addition to your M2 method, you could do:
// C# 4
var C = A.Zip(B, M2);
// C# 3 (not likely since Zip was introduced in .NET 4)
var C = A.Zip(B, (a, b) => M2(a, b));
var C = A.Zip<int, int, int>(B, M2);