Dividing a List into C # Blocks - c#

I have a Branch List, each one has a Number N of employees, I have a Branch object and a NumberEmployees property, now I need to iterate over that list sending the number of employees per block, I explain better with the following table: I order the List by Number of Employees, so far no problem.
+---------+-----------+
| Branch | Employees |
+---------+-----------+
|MEXICO | 800 |
|USA | 700 |
|INDIA | 500 |
|CHINA | 400 |
|AUSTRALIA| 300 |
+---------+-----------+
Now iterate through a list but dividing the number of employees into blocks something like this:
+-----------+------------+-------------+------------+
| Branch | FirstGroup | SecondGroup | ThirdGroup |
+-----------+------------+-------------+------------+
| Mexico | 267 | 267 | 267 |
| USA | 234 | 234 | 234 |
| India | 167 | 167 | 167 |
| China | 134 | 134 | 134 |
| Australia | 100 | 100 | 100 |
+-----------+------------+-------------+------------+
In the end I think the list that should result would be:
+-----------+-----------+
| Branch | Employees |
+-----------+-----------+
| Mexico | 267 |
| USA | 234 |
| India | 167 |
| China | 134 |
| Australia | 100 |
| Mexico | 267 |
| USA | 234 |
| India | 167 |
| China | 134 |
| Australia | 100 |
| Mexico | 267 |
| USA | 234 |
| India | 167 |
| China | 134 |
| Australia | 100 |
+-----------+-----------+
So far I can only order the List.
double TotalEmployees = ListBranch.Sum(item => item.EmployeeNumber);
double blockSize = TotalEmployees / ListBranch.Count();
double sizeQuery = Math.Ceiling(blockSize);
foreach (Branch branch in ListBranch.OrderByDescending(f => f. EmployeeNumber))
{
//to do
}
I appreciate your valuable help for any clues you can give me

This might do the trick for you
List<BranchEmployee> be = new List<BranchEmployee>();
be.Add(new BranchEmployee() { Branch = "MEXICO", Employee = 800 });
be.Add(new BranchEmployee() { Branch = "USA", Employee = 700 });
be.Add(new BranchEmployee() { Branch = "INDIA", Employee = 500 });
be.Add(new BranchEmployee() { Branch = "CHINA", Employee = 400 });
be.Add(new BranchEmployee() { Branch = "AUSTRALIA", Employee = 300 });
List<BranchEmployee> ExpectedBE = new List<BranchEmployee>();
for(int i = 0; i <= 2; i++)
{
foreach(BranchEmployee smbe in be)
{
ExpectedBE.Add(new BranchEmployee()
{
Branch = smbe.Branch,
Employee = smbe.Employee / 3
});
}
}
What I see is that every group has equal number of employees that is the total number of employees divided by 3.
To look the data in the way you have shown I have created a class like this
public class BranchEmployee
{
public string Branch { get; set; }
public int Employee { get; set; }
}

Related

Linq Group By and merge rows

I have a table similar to the following format:
id | name | year | Quality| Location |
------------------------------------------
1 | Apple | year1 | Good | Asia |
2 | Apple | year2 | Better | Asia |
3 | Apple | year3 | Best | Asia |
4 | Apple | year1 | Best | Africa |
5 | Apple | year2 | Bad | Africa |
6 | Apple | year3 | Better | Africa |
7 | Apple | year1 | Best | Europe |
8 | Apple | year2 | Bad | Europe |
9 | Apple | year3 | Better | Europe |
10 | Orange | year1 | Bad | Asia |
11 | Orange | year2 | Better | Asia |
12 | Orange | year3 | Bad | Asia |
13 | Orange | year1 | Best | Africa |
14 | Orange | year2 | Better | Africa |
15 | Orange | year3 | Bad | Africa |
16 | Orange | year1 | Best | Europe |
17 | Orange | year2 | Better | Europe |
18 | Orange | year3 | Best | Europe |
19 | Mango | year1 | Bad | Asia |
20 | Mango | year2 | Better | Asia |
21 | Mango | year3 | Better | Asia |
22 | Mango | year1 | Good | Africa |
23 | Mango | year2 | Better | Africa |
24 | Mango | year3 | Good | Africa |
25 | Mango | year1 | Best | Europe |
26 | Mango | year2 | Better | Europe |
27 | Mango | year3 | Best | Europe |
I need the list in this format in LINQ:
{ Location: Asia, year: year1, Good: 1, Bad: 2, Better: 0, Best: 0 }
{ Location: Asia, year: year2, Good: 0, Bad: 0, Better: 3, Best: 0 }
{ Location: Asia, year: year3, Good: 0, Bad: 1, Better: 1, Best: 1 }
.
.
.
.
My LINQ query is this:
var result = context.Fruits
.Groupby(f => new { f.Year,f.Location,f.Quality })
.Select(g => new
{
Year = g.Key.Year,
Location = g.Key.Location,
Quality = g.Key.Quality,
Count = g.Count()
});
This gives me something along the lines of:
{ Location: Asia, year: year1, Quality: Good, Count: 1 }
{ Location: Asia, year: year1, Quality: Bad, Count: 2 }
.
.
.
.
How do I get the required format with LINQ? Do I have to get the result and then use for each to get it to the format that I need?
You want to Count() the Quality property, so it does make sense to use it as a key in the grouping. If you just omit it and group only by Year and Location, you are getting the desired output.
public class Program
{
public static void Main()
{
var fruits = new List<Fruit> {
new Fruit { Id = 1, Name = "Apple", Year = "year1", Quality = "Good", Location ="Asia"},
new Fruit { Id = 2, Name = "Apple", Year = "year2", Quality = "Better", Location ="Asia"},
new Fruit { Id = 3, Name = "Apple", Year = "year3", Quality = "Better", Location ="Asia"},
new Fruit { Id = 4, Name = "Apple", Year = "year1", Quality = "Best", Location ="Africa"},
new Fruit { Id = 5, Name = "Orange", Year = "year1", Quality = "Vad", Location ="Asia"},
new Fruit { Id = 6, Name = "Orange", Year = "year2", Quality = "Better", Location ="Asia"},
new Fruit { Id = 7, Name = "Orange", Year = "year3", Quality = "Bad", Location ="Asia"},
};
var result = fruits.GroupBy(f => new { f.Year,f.Location})
.Select(g => new
{
Year = g.Key.Year,
Location = g.Key.Location,
Good = g.Count(x => x.Quality == "Good"),
Better = g.Count(x => x.Quality == "Better"),
Best = g.Count(x => x.Quality == "Best"),
});
foreach(var line in result) {
Console.WriteLine(String.Format("Year: {0} - Location: {1} - Good: {2} - Better: {3} - Best: {4}", line.Year, line.Location, line.Good, line.Better, line.Best));
}
}
}
public class Fruit
{
public int Id { get; set; }
public string Name { get; set; }
public string Year { get; set; }
public string Quality { get; set; }
public string Location { get; set; }
}
Output:
Year: year1 - Location: Asia - Good: 1 - Better: 0 - Best: 0
Year: year2 - Location: Asia - Good: 0 - Better: 2 - Best: 0
Year: year3 - Location: Asia - Good: 0 - Better: 1 - Best: 0
Year: year1 - Location: Africa - Good: 0 - Better: 0 - Best: 1
Fiddle: https://dotnetfiddle.net/eg99at

EF Code First Configuration Duplicating Records

So I'm attempting to populate a table with seed data in EF5. I have an Enum of all 50 states and DC. I also have a lookup table of RequestTypes with IDs 1-6. It would be something like this:
+----+----------+-------------+------------+
| Id | State | SurveyId | RequestType|
+----+----------+-------------+------------+
| 1 | Alabama | 0 | 1 |
| 2 | Alabama | 0 | 2 |
| 3 | Alabama | 0 | 3 |
| 4 | Alabama | 0 | 4 |
| 5 | Alabama | 0 | 5 |
| 6 | Alabama | 0 | 6 |
+----+----------+-------------+------------+
The model that represents this table:
public class StateSurveyAssignment{
public long Id { get; set; }
public string State { get; set; }
public long RequestTypeId { get; set; }
public long SurveyId { get; set; }
}
And the code to seed the database in the Configuration.cs:
foreach (var state in Enum.GetValues(typeof(State))) {
foreach (var type in context.RequestTypes){
context.StateSurveyAssignments.AddOrUpdate(
ssa => ssa.Id,
new StateSurveyAssignment{
State = state.ToString(),
RequestTypeId = type.Id
}
);
}
}
My problem is that instead of updating/doing nothing to unchanged records, the seed method is duplicating each row. I've attempted to manually set the Id but had no luck.
EDIT:
This is what the database duplication looks like:
+----+----------+-------------+------------+
| Id | State | SurveyId | RequestType|
+----+----------+-------------+------------+
| 1 | Alabama | 0 | 1 |
| 2 | Alabama | 0 | 2 |
| 3 | Alabama | 0 | 3 |
| 4 | Alabama | 0 | 4 |
| 5 | Alabama | 0 | 5 |
| 6 | Alabama | 0 | 6 |
| ...| ... | ... | ... |
|307 | Alabama | 0 | 1 |
|308 | Alabama | 0 | 2 |
|309 | Alabama | 0 | 3 |
|310 | Alabama | 0 | 4 |
|311 | Alabama | 0 | 5 |
|312 | Alabama | 0 | 6 |
+----+----------+-------------+------------+
My Solution
I swear I'd tried setting my own Id at some point but tried it again per the answer and it seems to have worked. My final solution:
int counter = 1;
foreach (var state in Enum.GetValues(typeof(State))) {
foreach (var type in context.RequestTypes){
context.StateSurveyAssignments.AddOrUpdate(
ssa => ssa.Id,
new StateSurveyAssignment{
Id = counter,
State = state.ToString(),
RequestTypeId = type.Id
}
);
counter++;
}
}
The problem could be that the Id property in your StateSurveyAssignment class is an Identity column in the database.
This means that each row is not unique.
For example you try to insert the following several times using AddOrUpdate()
var model = new StateSurveyAssignment
{
State = "Alabama",
RequestTypeId = 1L,
SurveyId = 0L
};
Then each entry would have a different Id and thus you'll have duplicates.

Merging 2 lists and sum several properties using LINQ

I have an class which contains the following properties:
public class SomeClass()
{
public Int32 ObjectId1 {get;set;}
public Int32 ObjectId2 {get;set;}
public Int32 ActiveThickeness {get;set;}
public Int32 ActiveFilterThickness {get;set;}
}
I also have 2 lists:
List<SomeClass> A
List<SomeClass> B
List A has data:
| ObjectId1 | ObjectId2 | ActiveThickness | ActiveFilterThickness |
-------------------------------------------------------------------
| 1 | 3 | 50 | 0 |
------------------------------------------------------------------
| 1 | 2 | 400 | 0 |
-------------------------------------------------------------------
| 4 | 603 | 27 | 0 |
-------------------------------------------------------------------
List B has data:
| ObjectId1 | ObjectId2 | ActiveThickness | ActiveFilterThickness |
-------------------------------------------------------------------
| 1 | 3 | 0 | 13671 |
------------------------------------------------------------------
| 1 | 2 | 0 | 572 |
-------------------------------------------------------------------
| 29 | 11 | 0 | 4283 |
-------------------------------------------------------------------
I want to merge A and B (using LINQ if possible) into List C of SomeCalss which contains data as followed:
| ObjectId1 | ObjectId2 | ActiveThickness | ActiveFilterThickness |
-------------------------------------------------------------------
| 1 | 3 | 50 | 13671 |
------------------------------------------------------------------
| 1 | 2 | 400 | 572 |
-------------------------------------------------------------------
| 29 | 11 | 0 | 4283 |
-------------------------------------------------------------------
| 4 | 603 | 27 | 0 |
-------------------------------------------------------------------
How can I achieve that?
Use GroupBy to group common objects and Sum to sum required properties
var ab = A.Concat(B).GroupBy(x => new
{
x.ObjectId1,
x.ObjectId2
});
var result = ab.Select(x => new SomeClass
{
ObjectId1 = x.Key.ObjectId1,
ObjectId2 = x.Key.ObjectId2,
ActiveFilterThickness = x.Sum(i => i.ActiveFilterThickness),
ActiveThickeness = x.Sum(i => i.ActiveThickeness)
});
See LINQ - Full Outer Join (SO).
By doing a left outer join and a right outer join, and then taking the union of those two, you should get what you're looking for.
var leftOuterJoin = from someclass1 in A
join someclass2 in B
on someclass1.ObjectID2 equals someclass2.ObjectID2
into temp
from item in temp.DefaultIfEmpty(new SomeClass(){ objectID1 = someclass1.objectID1, ... })
select new SomeClass()
{
...
};
var rightOuterJoin = from someclass2 in B
join someclass1 in A
on someclass1.ObjectID2 equals someclass2.ObjectID2
into temp
from item in temp.DefaultIfEmpty(new SomeClass(){ objectID1 = someclass1.objectID1, ... })
select new SomeClass()
{
...
};
var fullOuterJoin = leftOuterJoin.Union(rightOuterJoin);

Data grouping in SQL

I am working on a small project using C# and EF5.0 and I need to group some data. Let say I have table of columns in a building like shown below.
+----------+--------Columns Table--+------+------+
| ColumnID |ColumnName|Width|Length|Height|number|
+----------+----------+-----+------+------+------+
| 1 | C101 | 50 | 70 | 250 | 1 |
| 2 | C102 | 70 | 70 | 250 | 1 |
| 3 | C103 | 70 | 60 | 250 | 1 |
| 4 | C104 | 90 | 70 | 250 | 1 |
| 5 | C105 | 40 | 50 | 250 | 1 |
| 6 | C106 | 50 | 70 | 250 | 1 |
| 7 | C107 | 50 | 60 | 250 | 1 |
| 8 | C108 | 70 | 70 | 250 | 1 |
+----------+----------+-----+------+------+------+
I need a C# code to see the above data groupped like this:
+----------+---Groupped Columns Table-----+------+
|G_ColumnID|ColumnName|Width|Length|Height|number|
+----------+----------+-----+------+------+------+
| 1 |C(101-106)| 50 | 70 | 250 | 2 |
| 2 |C(102-108)| 70 | 70 | 250 | 2 |
| 3 | C103 | 70 | 60 | 250 | 1 |
| 4 | C104 | 90 | 70 | 250 | 1 |
| 5 | C105 | 40 | 50 | 250 | 1 |
| 6 | C107 | 50 | 60 | 250 | 1 |
+----------+----------+-----+------+------+------+
I prefer clues than the exact solution.
EDIT : Below code shows my current state. I think I can find the columns with the same Height, Width and Length using this code. But I am not sure how to generate a new name for the group.
using (pehlivanEntities context = new pehlivanEntities())
{
foreach (var item in context.table1)
{
int id = item.ColumnID;
foreach (var item2 in context.table1)
{
int id2 = item2.ColumnID;
if (id != id2)
{
if (item.Width == item2.Width)
{
if (item.Length == item2.Length)
{
if (item.Height == item2.Height)
{
//Alter item.ColumnName
//increase item.number by one
//Remove item2
}
}
}
}
}
}
}
Well you'd start with grouping on a composite key:
var groups = myData.GroupBy(d => new{d.Width, d.Length, d.Height})
then
groups
.Select(g => new {
g.Key.Width,
g.Key.Length,
g.Key.Height,
columnNames = g.Select(x => x.ColumnName),
number = g.Count()})
then a bit of string manipulation on the columnNames field

DataGridView Matching

I have 2 DataGridViews (DGVs).
theChipDGV will contain data like this (except with many more columns):
______________________________________________________________
| NAME | P/N | X | Y | ROTATION | PACKAGE |
|________|________|________|________|____________|___________|
| R16 | 147479 | 20.325 | 100.000| 0 | 0603 |
| C6 | 14739 | -5.325 | -10.105| 180 | 0603 |
| U45 | 123456 | 12.345 | 12.345 | 45 | 0402 |
|________|________|________|________|____________|___________|
theDataBaseDGV will contain data like this (except with many more columns):
____________________________________________________________________________________________
| PACKAGE | DESCRIPTION | FEEDER | VISION | SPEED | MACHINE | WIDTH | TIME |
|___________|_______________|__________|__________|_________|___________|_________|_______ |
| PLCC20 | N/A | 25MM | N/A | 3 | UNIVERSAL | 12MM | 0.05 |
| 0603 | 0603C_1.0 | 8X4 | 1 | 1 | FUJI-1 | 8MM | 20 |
| 0603 | 0603R_1.0 | 12X4 | 1 | 5 | FUJI-2 | 16MM | 0.20 |
|___________|_______________|__________|__________|_________|___________|_________|_______ |
What I would like to do is match the column in theChipDGV labeled PACKAGE with the same labeled column in theDataBaseDGV. If there is a match, the entire row will be concatted into a new DGV (let's label it: theFinalDGV). Also, if the PACKAGE type is matched and is also in the next line (like 0603) it will check to see if the column labeled Name in theChipDGV starts with a R or a C. Depending on which it starts with will determine the rest of the columns from theDataBaseDGV that will be used.
SO:
theFinalDGV will look like this:
_____________________________________________________________________________________________________________________________________________
| NAME | P/N | X | Y | ROTATION | PACKAGE | DESCRIPTION | FEEDER | VISION | SPEED | MACHINE | WIDTH | TIME |
|________|________|________|________|____________|___________|_______________|__________|__________|_________|___________|_________|________|
| R16 | 147479 | 20.325 | 100.000| 0 | 0603 | 0603R_1.0 | 12X4 | 1 | 5 | FUJI-2 | 16MM | 0.20 |
| C6 | 14739 | -5.325 | -10.105| 180 | 0603 | 0603C_1.0 | 8X4 | 1 | 1 | FUJI-1 | 8MM | 20 |
| U45 | 123456 | 12.345 | 12.345 | 45 | 0402 | | | | | | | |
|________|________|________|________|____________|___________|_______________|__________|__________|_________|___________|_________|________|
Notice, if there is no match it leaves the columns empty.
So:
Does anyone know how I can possibly go about doing this? I mostly would like to know how to match the values from 1 column with another and if there are multiple columns from theDataBaseDGV that have the same values.. then how to properly match those.
int chipRowCount = theChipDGV.RowCount;
int dataBaseRowCount = theDataBaseDGV.RowCount;
for (int count = 0; count < chipRowCount; count++)
{
for (int i = 0; i < dataBaseRowCount; i++)
{
if (theChipList[count].PkgStyle.Equals(theDataBaseList[i].PackageType) || (theChipList[count].PkgStyle.Contains(theDataBaseList[i].PackageType) &&
theDataBaseList[i].PartDescription.Contains("R") && theChipList[count].Name.StartsWith("R")) || ((theChipList[count].PkgStyle.Contains(theDataBaseList[i].PackageType) &&
theDataBaseList[i].PartDescription.Contains("C") && theChipList[count].Name.StartsWith("C"))))
{
if (!theChipList[count].Used)
{
theChipList[count].Used = true;
theFinalList.Add(new LoadLine(theChipList[count].Name, theChipList[count].PartNumber,
theChipList[count].XPlacement, theChipList[count].YPlacement, theChipList[count].Rotation,
theChipList[count].PkgStyle, theDataBaseList[i].PackageType, theDataBaseList[i].PartDescription,
theDataBaseList[i].Feeder, theDataBaseList[i].Vision, theDataBaseList[i].Speed,
theDataBaseList[i].Machine, theDataBaseList[i].TapeWidth, 0));
theFinalDGV.DataSource = theFinalList;
}
}
}
}
for (int count = 0; count < theChipList.Count; count++)
{
if (!theChipList[count].Used)
{
theFinalList.Add(new LoadLine(theChipList[count].Name, theChipList[count].PartNumber,
theChipList[count].XPlacement, theChipList[count].YPlacement, theChipList[count].Rotation,
theChipList[count].PkgStyle, string.Empty, string.Empty, string.Empty, string.Empty,
string.Empty, string.Empty, string.Empty, 0));
}
}

Categories

Resources