How to select properties that are not in groupby - c#

I'm trying to select some properties that are not in my group by. Is this possible?
var r = from w in drinks.ToList()
group w by new{
w.DrinkId
} into wGroup
select new DrinksValue(){
DrinkId = wGroup.DrinkId,
CatId = // trying to pull from wGroup???
Lasting = // trying to pull from wGroup???
DrinkTypes = wGroup.Select( x => new DrinkAttributes { Taste = wGroup.Key.Flavor, Benefit = wGroup.Sum(x => x.Value)
}).ToList()
};
How would I get CatId, and Lasting properties from the drinks.ToList()?
If I add them to the group like w.DrinkId, w.Flavor, w.Value, w.CatId, w.Lasting it messes up my results.
I can get other properties like Value using .Sum , but these properties aren't computed values.
Example of data:
DrinkId | CatId | Lasting | Flavor | Value
------------------------------------------
1 2 5.1 orange 11.0
1 2 5.1 blue 09.0
1 2 6.0 red 10.0
2 3 6.0 red 10.0
2 3 6.0 yellow 01.0
Want the results to be grouped like this:
{
"DrinkId": 1,
"CatId": 2,
"Lasting": 5.1,
"DrinkTypes": [{ Taste: orange, Benefit: 11.0}, {Taste: blue, Benefit: 09.0}, {Taste: red, Benefit: 10.0}]
}

This query should work:
var query =
from w in drinks
group w by new { w.DrinkId, w.CatId, w.Lasting } into wGroup
select new DrinksValue
{
DrinkId = wGroup.Key.DrinkId,
CatId = wGroup.Key.CatId,
Lasting = wGroup.Key.Lasting,
DrinkTypes = wGroup.Select(x => new DrinkAttributes
{ Taste = x.Flavor, Benefit = x.Value }
).ToList()
}
};
var r = query.ToList()

Related

How to remove objects that have 3 properties the same only leaving 1 but adding the number of those duplicates to a nr. column of that 1

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

Calculate 2 table where condition - Linq

i have 2 table ,
produk table
id produk batch qty
1 AAA ADADAD 2
2 BBB ADADAD 2
3 BBB AAAAAA 2
...............
and move table,
id produk batch qty
1 BBB ADADAD 1
and i want showing table after qty from stok table minus qty from move table, what i want table
PRODUK BATCH QTY
AAA ADADAD 2
BBB ADADAD 1
BBB AAAAAA 2
and this my query
var obj = _db.produk
.Groupby(a=> new {a.code,a.batch})
.Select(a=> new {
produk = a.key.code,
batch = a.Key.batch,
qty = _db.move.Where(c => a.Any(p => p.code == a.code && p.batch == a.batch)).Sum(a=>a.qty)
}).tolist();
but not working
You have to do LEFT JOIN to grouped move table.
var moves =
from m in _db.move
group m by { m.code, m.batch } into g
select
{
g.Key.code,
g.Key.batch,
qty = g.Sum(x => x.qty)
};
var query =
from p in _db.produk
join m in moves on new { p.code, p.batch } equals { m.code, m.batch } into j
from m in j.DefaultIfEmpty()
select new
{
produk = p.code,
batch = p.batch.
qty = p.qty - (int?)m.qty ?? 0
};
If you prefer method syntax over query syntax then you can write your query as this:
var availableItems = repository
.GroupJoin(purchases,
stock => new { stock.Product, stock.Batch },
move => new { move.Product, move.Batch },
(stock, moves) => new { Stock = stock, Moves = moves })
.SelectMany(
stockAndRelatedMoves => stockAndRelatedMoves.Moves.DefaultIfEmpty(),
(stockAndRelatedMoves, relatedMove) => new
{
stockAndRelatedMoves.Stock.Product,
stockAndRelatedMoves.Stock.Batch,
Quantity = stockAndRelatedMoves.Stock.Quantity - (relatedMove?.Quantity ?? 0)
})
.ToList();
As you can see instead of GroupBy you need to use GroupJoin and instead of simple Select you need SelectMany to retrieve items from the joined records.
Some explanation:
stock => new { stock.Product, stock.Batch }: Anonymous type is used here because multiple fields are used in the join
stockAndRelatedMoves => stockAndRelatedMoves.Moves.DefaultIfEmpty(): it is needed because of left outer join
(relatedMove?.Quantity ?? 0): relatedMove can be null that's why we substitute it with 0
In the above code I've used the following collections:
var repository = new List<Stock>
{
new Stock { Id = 1, Product = "AAA", Batch = "ADADAD", Quantity = 2 },
new Stock { Id = 2, Product = "BBB", Batch = "ADADAD", Quantity = 2 },
new Stock { Id = 3, Product = "BBB", Batch = "AAAAAA", Quantity = 2 },
};
var purchases = new List<Move>
{
new Move { Id = 1, Product = "BBB", Batch = "ADADAD", Quantity = 1 }
};
You could also query the produck table, then, in the Select statement, filter the move table based on the produck's batch and produck properties, then calculate the qty.
Code as below:
List<Produk> produks = new List<Produk>()
{
new Produk(){ id = 1, produk= "AAA", batch="ADADAD", qty = 2},
new Produk(){ id = 2, produk= "BBB", batch="ADADAD", qty = 2},
new Produk(){ id = 3, produk= "BBB", batch="AAAAAA", qty = 2},
};
List<Remove> removes = new List<Remove>()
{
new Remove(){ id=1, produk="BBB", batch="ADADAD", qty=1}
};
var result = (from p in produks
select new Produk
{
id = p.id,
produk = p.produk,
batch = p.batch,
qty = p.qty - removes.Where(c => c.produk == p.produk && c.batch == p.batch).Sum(c => c.qty)
}).ToList();
The result like this:

return different lists for each key

I have a table that is parsed in code as a List of string[] (each string[] is a column).
Column1| Column2 | Column3
--------+---------+----------
0 | 1 | 8
3 | 2 | 3
5 | 2 | 8
Let´s say:
string[] column1 = { 0, 3, 5 }
string[] column2 = { 1, 2, 2 };
string[] column3 = { 8, 3, 8 };
List<string[]> table = new List<string[]>() { column1, column2, column3 };
I want to select a column (i.e. Column1) groupby Column3, and create a list with each different value in Column3. In other words: group Column1 by column3 and create a Column for each different value of Column3.
The output would be:
string[] result1 = { 3 }; // From column3[1] = 3
string[] result2 = { 0, 5 }; // From column3[0] = column3[2] = 8
It´s a mix of this post, this one, and Simple 1 from msdn.
I think about creating a object with column1 and column3, and then do as this post:
Class Row { public Row(string row1, string row3); }
List<Row> rows = new List<Row>();
for(int i = 0; i < column1.Length; i++)
{ rows.Add(new Row(Column1[i], Column3[i])); }
var output = rows.GroupBy(row => row.row3).ToDictionary(grp => grp.Key, grp => grp.ToList());
But this code code is a bit ugly. Isn´t there something like
column3.selectmany(...).GroupBy(row => row.row3).ToDictionary(grp => grp.Key, grp => grp.ToList());
I mean, some expression without the need of creating a new class and fill a list of objects... Also, I want as output
string[] result1 = { 3 }; // From column3[1] = 3
string[] result2 = { 0, 5 }; // From column3[0] = column3[2] = 8
Instead of creating a new type just for grouping you can use the Zip extension method and an anonymous type to create the rows.
Grouping is pretty straightforward then. Each group has a key which represents column3 and the IGrouping itself is an IEnumerable containing the rows from which you select only column 1:
var rows = column1.Zip(column3, (c1, c3) => new
{
Column1 = c1,
Column3 = c3
});
var output = from row in rows
group row by row.Column3 into groupedRows
select groupedRows.Select(r => r.Column1).ToArray();
This produces an IEnumerable<string[]>.
pescolino's answer using Zip is quite nice. If you can't use it (e.g. if you are not on .NET 4.x) then you could use the indexer-overload of Select to produce the desired result:
var res = col1.Select((fld,idx) => new { Field = fld, Index = idx })
.GroupBy(entry => col3[entry.Index], entry => entry.Field)
.Select(grp => grp.ToArray());

C# Group with conditional

how can i grouping a data with conditional if bill < 10 ?
i have table:
meetingId | bill
------------------
a | 6
b | 7
c | 1
a | 5
a | 3
b | 4
g | 2
expected results :
a = 6+5+3 = 14 limit is 10 --> 10 and 4
b = 7+4 = 11 so limit is 10 --> 10 and 1
c and g not over the limit.
meetingId | bill
------------------
a | 10
a | 4
b | 10
b | 1
c | 1
g | 2
i tried in SQL why but i stuck with if condition
my SQL :
SELECT NO_ORDRE
,ORDRE.CODE_CLIENT As CodeCl
,[CODE_DEST]
,ORDRE.RS_NOM As OrdreRS
,ORDRE.ADRESSE As OrdreAdr
,ORDRE.CP As OrdreCP
,ORDRE.VILLE As OrdreVille
,ENLEV_CREMB
,ENLEV_DECL
,MODAL_MODE
,[PAYS]
,[INSEE]
,[SIRET]
,ORDRE.TEL As OrdreTel
,ORDRE.FAX As OrdreFax
,[EMAIL]
,[NBR_COLIS]
,[POID]
,[OBS]
,[DATE_CREE]
,[DATE_MODIF]
,[REF_EXPED]
,[AUTRE_REF]
,[AGENCE]
,[TRANSPORTEUR]
,NOM
,CAPITAL
,LIBELLE
,T_LOGO.IMG As FaImg
,T_LOGO.ADRESSE As FaAdr
,T_LOGO.CP As FaCp
,T_LOGO.VILLE As FaVille
,T_LOGO.TEL As FaTel
,T_LOGO.FAX As FaFax
,FAWEB_CLIENT.RS_NOM As CliRsNom
,FAWEB_CLIENT.ADRESSE As CliAdr
,FAWEB_CLIENT.CP As CliCp
,FAWEB_CLIENT.VILLE As CliVille
FROM [ORDRE]
LEFT JOIN T_LOGO ON ORDRE.TRANSPORTEUR = T_LOGO.NOID
LEFT JOIN FAWEB_CLIENT ON ORDRE.CODE_CLIENT = FAWEB_CLIENT.CODE_CLIENT
WHERE (STATUT_ORDRE = 2) AND (TRANSPORTEUR IN (SELECT ParsedString From dbo.ParseStringList(#Trans)))
and then i use in C#
List<Pers_Ordre> oListOrdre = new List<Pers_Ordre>();
while (readerOne.Read())
{
Pers_Ordre oPerOrdr = new Pers_Ordre();
Pers_Ordre test = (from t in oListOrdre where t.DestId == readerOne["CODE_DEST"].ToString() select t).FirstOrDefault();
oPerOrdr.OrdreId = Convert.ToInt32(readerOne["NO_ORDRE"]);
oPerOrdr.DestId = readerOne["CODE_DEST"].ToString();
if (test == null)
{
oListOrdre.Add(oPerOrdr);
}
else
{
int NbrColis = (from t in oListOrdre where t.DestId == readerOne["CODE_DEST"].ToString() select t.NbrColis).FirstOrDefault();
if (NbrColis < 5)
{
test.NbrColis += NbrColis;
}
}
}
it not work what i expected.
Thanks for your help!
(Not really an answer, but this doesn't fit in a comment.)
Here's a LINQ-to-Objects query that groups items by meetingId and creates new items such that there is one item with bill less than 10 and as many items as needed with bill equalling 10 to keep the sum:
Is this what you're looking for?
Code:
var list = new List<Tuple<char, int>>
{
Tuple.Create('a', 6),
Tuple.Create('b', 7),
Tuple.Create('c', 1),
Tuple.Create('a', 5),
Tuple.Create('a', 3),
Tuple.Create('b', 4),
Tuple.Create('g', 2),
};
var result = list
.GroupBy(x => x.Item1)
.Select(g => new
{
Key = g.Key,
Sum = g.Sum(x => x.Item2)
})
.Select(p => new
{
Key = p.Key,
Items = Enumerable.Repeat(10, p.Sum / 10)
.Concat(Enumerable.Repeat(p.Sum % 10, 1))
})
.SelectMany(p => p.Items.Select(i => Tuple.Create(p.Key, i)))
.ToList();
This SQL query will return the wanted results:
SELECT meetingId, SUM(bill) as bill_total
FROM table
GROUP BY meetingId
HAVING SUM(bill) < 10
You should not do this at the client side because it can get pretty intensive, a simple GROUP BY with a HAVING clause should give you the expected results:
Sample data:
The query you need:
SELECT
MeetingID,
SUM(bill) AS Total
FROM
Table_1
GROUP BY
MeetingID
HAVING
SUM(bill) < 10
The results of the query:
table.GroupBy(p => p.meetingId).Where(p => p.Sum(q => q.bill) < 10)
.Select(p => new
{
meetingId= p.Key,
bill= p.Sum(q => q.bill)
});

C# Linq Union On Attribute

I have two linq queries that I want to unionize on a common attribute:
One
{
Id,
Name,
Color
}
Two
{
Color,
Cost
}
I want to get the union of One and Two by unionizing on Color? If there is not a Two with a Color that corresponds to One, I want to set Cost to 0 in the output? How do I do this in LINQ?
Here is a sample using anonymous types on how to perform a left outer join:
var products = new[] {
new { Id = 1, Name = "Alpha", Color = "Red" },
new { Id = 2, Name = "Beta", Color = "Green" },
new { Id = 3, Name = "Gamma", Color = "Blue" }
};
var costs = new[] {
new { Color = "Red", Cost = 100 },
new { Color = "Blue", Cost = 200 },
new { Color = "Blue", Cost = 300 }
};
var query = products
.GroupJoin(
costs, p => p.Color, c => c.Color,
(p, c) => new { p.Id, p.Name, p.Color, Costs = c.DefaultIfEmpty() }
)
.SelectMany(
gj => gj.Costs,
(gj, c) => new { gj.Id, gj.Name, gj.Color, Cost = c == null ? 0 : c.Cost }
);
Query result:
Id Name Color Cost
-------------------
1 Alpha Red 100
2 Beta Green 0
3 Gamma Blue 200
3 Gamma Blue 300
This is called a join, not a union.
See the documentation.
You want a left outer join to keep the values appearing in the first list but are not present in the second.

Categories

Resources