Splitting hierarchies from datatable - c#

I have a datatable which contains multiple hierarchies with different heights which I need to split.
eg.
|---------------------|------------------|
| Account | Hierarchy Account|
|---------------------|------------------|
| 1 | |
|---------------------|------------------|
| 2 | 1 |
|---------------------|------------------|
| 3 | 1 |
|---------------------|------------------|
| 4 | 2 |
|---------------------|------------------|
| 5 | 3 |
|---------------------|------------------|
| 6 | |
|---------------------|------------------
| 7 | 6 |
|---------------------|------------------|
| 8 | 6 |
|---------------------|------------------|
Below is what I have tried so far.
private List<DataTable> SplitDataTablesOnHierarchy(DataTable dataTable)
{
List<DataTable> dataTablesList = new List<DataTable>();
List<string> listTemp = new List<string>();
var HierarchyAccounts = dataTable.AsEnumerable().Where(m => m.Field<string>("Hierarchy Account Number") == "");
foreach(var topAccount in TopAccounts )
{
//Check if account exists in Hierarchy Account Number
var topAccountExists = dataTable.AsEnumerable().Any(m => m.Field<string>("Hierarchy Account Number") == topAccount.Field<string>("Account Number"));
if (topAccountExists == true)
{
DataTable newDataTable = dataTable.Clone();
newDataTable.ImportRow(payerAccount);
dataTablesList.Add(newDataTable);
}
//Top Accounts found and added to tempList
}
//CreateDataTable with Top Accounts
foreach(DataTable dTable in dataTablesList)
{
bool bottomHierarchyReached = true;
var TempSearch = dTable.Rows;
while(bottomHierarchyReached)
{
foreach(DataRow account in TempSearch)
{
var rows = dataTable.AsEnumerable().Where(m => m.Field<string>("Hierarchy Account Number") == account.Field<string>("Account Number")).CopyToDataTable();
if(rows.Rows.Count == 0)
{
bottomHierarchyReached = false;
break;
}
else
{
TempSearch = rows.Rows;
dTable.Rows.Add(rows.Rows);
}
}
}
}
return dataTablesList;
}
My thought process above was to first find the highest accounts in the hierarchy, create new datatables with those accounts and then drill down and add the following levels to the relevant datatable recursively since I do not know the height of each hierarchy.

Found a solution by creating a tempList which keeps the all of the lower levels while searching through the level above.
Once the loop through the SearchList is done we assign the tempList to it.
And then search through the next level of the hierarchy.
foreach (DataTable dTable in dataTablesList)
{
bool bottomHierarchyReached = true;
var SearchList = dTable.AsEnumerable().Select(p=> new { HierarchyAccount = p.Field<string>("Hierarchy Account Number"),
Account = p.Field<string>("Account Number")
}).ToList();
var tempList = SearchList.ToList();
tempList.Clear();
while (bottomHierarchyReached)
{
tempList.Clear();
foreach (var account in SearchList)
{
var rows = dataTable.AsEnumerable().Where(m => m.Field<string>("Hierarchy Account Number") == account.Account);
if(rows.Count() == 0)
{
bottomHierarchyReached = false;
break;
}
else
{
tempList.AddRange(rows.AsEnumerable().Select(p => new {
HierarchyAccount = p.Field<string>("Hierarchy Account Number"),
Account = p.Field<string>("Account Number")
}).ToList());
foreach(var row in rows)
{
dTable.ImportRow(row);
}
}
}
SearchList = tempList.ToList();
}
}

Related

Best way to search for persons in location

I have 3 tables that holds the locations, users, with their relevant Ids.
What I'm trying to achieve is to allow an admin to be able to search for
users in a particular location.
So in short, I'm trying to allow Sam who is the Admin to locate users only in his location(restricted to his location), so in this example, Mark who is a regular user is located in HeadLocation and Location1 and can be found by Sam.
User Table
|---------------------------------|
| UserId | Role | FName |
|---------------------------------|
| 1 | Admin | Sam |
OfficeUser Table
|---------------------------------------|
| OfficeUserId | OfficeId | UserId |
|---------------------------------------|
| 55 | 1 | 1 |
Office Table
|----------------------------------------------|
| OfficeId | HeadOfficeId | LocNames |
|----------------------------------------------|
| 1 | null | HeadLocation |
|----------------------------------------------|
Code
[HttpGet]
public IActionResult SearchUsers(string searchUser, string id)
{
//userId from claims
var userId = User.FindFirst(ClaimTypes.NameIdentifier).Value;
//gets userId for users with role of admin
var admin = _context.User.FirstOrDefault(a => a.Role == "Admin")?.UserId.ToString();
if (adminExists && admin == userId)
{
//This is where I'm unclear...
var search = from a in _context.OfficeUser
join b in _context.Office
on a.OfficeId equals a.OfficeId
select new
{
a.UserId,
//trying to get names per locations
};
if (search == null)
{
return NotFound();
}
if (!String.IsNullOrEmpty(searchUser))
{
search = search.Where(a => a.FName.Contains(searchUser));
}
else
{
return NotFound("No Users Found");
}
}
return Ok(search);
}
This will be the generalized view of your query. There can some typing mistakes as I have typed this in here, not in visual studio editor.
[HttpGet]
public IActionResult SearchUsers(string searchUser, string id)
{
var loggedInUserId = User.FindFirst(ClaimTypes.NameIdentifier).Value;
var isLoggedInUserAdmin = _context.User.Any(u => u.UserId == loggedInUserId && u.Role == "Admin");
if (isLoggedInUserAdmin)
{
var adminLocations = _context.OfficeUser.Where(ou => ou.UserId == userId).Select(ou => ou.Office.LocNames).ToList();
var searchQuery = _context.OfficeUsers.Where(ou => adminLocations.Contains(ou.Office.LocNames)).Select(ou => ou.User);
if (!String.IsNullOrEmpty(searchUser))
{
searchQuery = searchQuery.Where(q => q.FName.Contains(searchUser));
}
var adminLocUserList = searchQuery.ToList();
return Ok(userList)
}
var userList = _context.OfficeUsers.Where(ou.User.FName.Contains(searchUser)).Select(ou => ou.User).ToList();
return Ok(userList);
}

Transform SQL Records to Object

Currently I am developing an application like SharePoint and I am encountering a difficult as followed.
I have a DB table to keep my contents like the following
+----+---------------+----------+---------+----------+--------------------------+
| ID | Content_type | List_ID | COL_ID | ITEM_ID | VALUE |
+----+---------------+----------+---------+----------+--------------------------+
| 1 | "Column" | 1 | 0 | | "ABC" |
| 2 | "Column" | 1 | 1 | | "DEF" |
| 3 | "Item" | 1 | 0 | 1 | "<VALUE OF Column ABC>" |
| 4 | "Item" | 1 | 1 | 1 | "<VALUE OF Column DEF>" |
+----+---------------+----------+---------+----------+--------------------------+
and I would like to display these record on the web using linq and C# like the following....
ITEM_ID |ABC |DEF
------------+---------------------+----------------------
1 |<VALUE OF Column ABC>|<VALUE OF Column DEF>
EDITED:
My questions are:
I would like to use the DB record stated as Column in the content_type field to be the DataColumn of a DataTable.
I would like to map all records in the DB stated as ITEM with the same Item_ID as 1 DataRow of a DataTable. The value field of each DB records will fall onto the column of the DataTable based on the Column ID.
Thanks for the help. I had make it by myself....
First get the records from DB where content_type = "Column" and use
these records to form a DataTable
Get all records from DB where content_typ= "Item" and re-group each item to a List where ITEM_id =
Map the Item.title = column of the DataTable and Items.value = value of the DataTable rows....
public static DataTable PopulateRec(int list_id, string web_app, string host_auth)
{
DataTable dt = new DataTable();
List Column = Get_Column(list_id, web_app, host_auth);
for (int i = 0; i < Column.Count(); i++)
{
DataColumn datacolumn = new DataColumn();
datacolumn.ColumnName = Column[i].ToString();
dt.Columns.Add(datacolumn);
}
List Items = Get_Item(list_id, web_app, host_auth);
if (Items.Count != 0)
{
int ItemCount = Convert.ToInt32((from Itms in Items
select Itms.Item_id).Max());
for (int j = 0; j <= ItemCount; j++)
{
dt.Rows.Add();
List IndvItem = (from Indv in Items
where Indv.Item_id == j
select Indv).ToList();
foreach (var val in IndvItem)
{
dt.Rows[j][val.title] = val.value;
}
IndvItem = null;
}
for (int k = 0; k < dt.Rows.Count; k++)
{
if (dt.Rows[k][0].ToString() == string.Empty)
{
dt.Rows[k].Delete();
}
}
}
Column = null;
Items = null;
return dt;
}
private static List Get_Column(int list_id, string web_app, string host_auth)
{
List content_db = new List();
List columnname = new List();
Config_DB_Context configdb = new Config_DB_Context("dms_config");
content_db = (from c in configdb.site_mapping
where c.host_auth == host_auth
&& c.web_app == web_app
select c.content_db).ToList();
for(int i = 0; i < content_db.Count(); i++)
{
Content_DB_Context contentdb = new Content_DB_Context(content_db[i]);
columnname = (from c in contentdb.content
where c.content_type == "Column"
&& c.list_id == list_id
select c.title).ToList();
}
content_db = null;
return columnname;
}
private static List Get_Item(int list_id, string web_app, string host_auth)
{
List content_db = new List();
List Itm = new List();
Config_DB_Context configdb = new Config_DB_Context("dms_config");
content_db = (from c in configdb.site_mapping
where c.host_auth == host_auth
&& c.web_app == web_app
select c.content_db).ToList();
for (int i = 0; i < content_db.Count(); i++)
{
Content_DB_Context contentdb = new Content_DB_Context(content_db[i]);
Itm = (from c in contentdb.content
where c.content_type == "Item"
&& c.list_id == list_id
select new MyItem
{
col_id = (int)c.column_id,
list_id = (int)c.list_id,
title = c.title,
value = c.value,
Item_id = (int)c.item_id,
hidden = c.hidden
}).ToList();
}
content_db = null;
return Itm;
}

Reading in list using SqlDataReader C#

I have to fill in some lists in while loop as:
while (_myReader_1.Read())
{
_Row_Counter++;
int _authorID = _myReader_1.GetInt32(0);
Author _author = _eAthors.FirstOrDefault(_a => _a._AuthorID == _authorID);
if (_author == null)
{
_author = new Author
{
_AuthorID = _authorID,
_AuthorName = _myReader_1.GetString(1),
_Attributes = new List<AuthorAttributes>()
};
}
var _attribute = new AuthorAttributes()
{
_PaperID = new List<int>(),
_CoAuthorID = new List<int>(),
_VenueID = new List<int>()
};
_attribute._PaperID.Add(_myReader_1.GetInt32(2));
_attribute._CoAuthorID.Add(_myReader_1.GetInt32(3));
_attribute._VenueID.Add(_myReader_1.GetInt32(4));
_attribute._Year = _myReader_1.GetInt32(5);
_author._Attributes.Add(_attribute);
_eAthors.Add(_author);
}
_myReader_1.Close();
The data in SQL table looks like:
Author_ID | Author_Name | Paper_ID | CoAuthor_ID | Venue_ID | Year
------------------------------------------------------------------
677 | Nuno Vas | 812229 | 901706 | 64309 | 2005
677 | Nuno Vas | 812486 | 901706 | 65182 | 2005
677 | Nuno Vas | 818273 | 901706 | 185787 | 2005
677 | Nuno Vas | 975105 | 901706 | 113930 | 2007
677 | Nuno Vas | 975105 | 1695352 | 113930 | 2007
... | ... | ... | ... | ... | ...
The issue is each time loop iterates, new lists _PaperID, _CoAuthorID and _VenueID are created, which is not desired. As we have a check if(author == null), then to create a new author, similarly I want to check if a list for _PaperID exists for an author e.g. for Author_ID = 677, then to Add in same list until Author_ID get changed.
Also until the Author_ID = 677, the list _eAuthors should have Count = 1
I'm attaching some images to refine the problem.
Image 1: Showing eAuthors Count = 3, Attributes Count = 3 for AuthorID = 677, while 3 of iterations passed whereas eAuthors Count should = 1.
Image 2: Showing Individual Attribute lists for each row, as in 3rd iteration the Attribute e.g. CoAuthorID, the Count = 1, whereas it should be = 3 while in 3rd iteration and same for rest of the Attributes
Following the data structure shown and seeing what depicted in images, it seems that all attributes (Paper, CoAuthor, Venue) are of type lists, so there is no need to declare attributes as List<AuthorAttributes>. Follow this to what you want to achieve:
while (_myReader_1.Read())
{
_Row_Counter++;
int _authorID = _myReader_1.GetInt32(0);
Author _author = _eAthors.FirstOrDefault(_a => _a._AuthorID == _authorID);
if (_author == null)
{
_author = new Author
{
_AuthorID = _authorID,
_AuthorName = _myReader_1.GetString(1),
_Attributes = new AuthorAttributes()
};
}
// Check if list _PaperID doesn't exist
if (_author._Attributes._PaperID == null)
{
// Create new _PaperID
_author._Attributes._PaperID = new List<int>();
// Add Paper_ID to _PaperID
_author._Attributes._PaperID.Add(_myReader_1.GetInt32(2));
}
else // Add Paper_ID to existing _PaperID list
_author._Attributes._PaperID.Add(_myReader_1.GetInt32(2));
// Check if list _CoAuthorID doesn't exist
if (_author._Attributes._CoAuthorID == null)
{
// Create new _CoAuthorID
_author._Attributes._CoAuthorID = new List<int>();
// Add CoAuthor_ID to _CoAuthorID
_author._Attributes._CoAuthorID.Add(_myReader_1.GetInt32(3));
}
else // Add CoAuthor_ID to existing _CoAuthorID list
_author._Attributes._CoAuthorID.Add(_myReader_1.GetInt32(3));
// Check if list _CoAuthorID doesn't exist
if (_author._Attributes._VenueID == null)
{
// Create new _VenueID
_author._Attributes._VenueID = new List<int>();
// Add Venue_ID to _VenueID
_author._Attributes._VenueID.Add(_myReader_1.GetInt32(4));
}
else // Add Venue_ID to existing _VenueID list
_author._Attributes._VenueID.Add(_myReader_1.GetInt32(4));
// Add Year to _Year
_author._Attributes._Year =_myReader_1.GetInt32(5);
if (!_eAthors.Contains(_author))
_eAthors.Add(_author);
}
_myReader_1.Close();
Assuming your data structure looks like this:
Author
AuthorAttributes
Papers (list)
PaperID
CoAuthors (list)
CoAuthorID
Venues (list)
VenueID
Year
You could try this:
while (_myReader_1.Read())
{
_Row_Counter++;
int _authorID = _myReader_1.GetInt32(0);
string _authorName = _myReader_1.GetString(1);
int _paperID = _myReader_1.GetInt32(2);
int _coAuthorID = _myReader_1.GetInt32(3);
int _venueID = _myReader_1.GetInt32(4);
int _year = _myReader_1.GetInt32(5);
Author _author = _eAthors.FirstOrDefault(_a => _a._AuthorID == _authorID);
if (_author == null)
{
_author = new Author
{
_AuthorID = _authorID,
_AuthorName = _authorName,
_AuthorAttributes = new AuthorAttributes
{
_Papers = new List<int>(),
_Venues = new List<int>(),
_Year = _year,
_CoAuthors = new List<int>()
}
};
_eAthors.Add(_author); // only add if author not found
}
if ( !_author._AuthorAttributes._Papers.Contains( _paperID ) )
_author._AuthorAttributes._Papers.Add( _paperID );
if ( !_author._AuthorAttributes._CoAuthors.Contains( _coAuthorID ) )
_author._AuthorAttributes._CoAuthors.Add( _coAuthorID );
if ( !_author._AuthorAttributes._Venues.Contains( _venueID ) )
_author._AuthorAttributes._Venues.Add( _venueID );
}
_myReader_1.Close();
Add the newly created author right after initialization in the author == null check. Then check if author.PaperID == null and if it is, add the AuthorAttributes. Like this:
while (_myReader_1.Read())
{
_Row_Counter++;
int _authorID = _myReader_1.GetInt32(0);
Author _author = _eAthors.FirstOrDefault(_a => _a._AuthorID == _authorID);
if (_author == null)
{
_author = new Author
{
_AuthorID = _authorID,
_AuthorName = _myReader_1.GetString(1),
_Attributes = new List<AuthorAttributes>()
};
_eAthors.Add(_author); // ********** Add the new author
}
// Watch out!!! author.Attributes may be null for existing authors!!!
if (author.Attributes.PaperID == null || author.PaperID.Count == 0) // Check for PaperID existence
{
var _attribute = new AuthorAttributes()
{
_PaperID = new List<int>(),
_CoAuthorID = new List<int>(),
_VenueID = new List<int>()
};
_attribute._PaperID.Add(_myReader_1.GetInt32(2));
_attribute._CoAuthorID.Add(_myReader_1.GetInt32(3));
_attribute._VenueID.Add(_myReader_1.GetInt32(4));
_attribute._Year = _myReader_1.GetInt32(5);
_author._Attributes.Add(_attribute);
}
}
_myReader_1.Close();
Of course, if necessary you can process each attribute separately, by adding an if block for each one.

Implementing COUNT() and ROUND() method in LINQ query

For another example to get return data of a pivot table I'm defined a LINQ query to solve this problem. Well, now my question is how to count the values of a column?
Here the following C# Code:
var query = from q in db.DS
where q.datum >= fromDate && q.datum <= toDate
group q by q.quot_rate
into grp
select new
{
Grade = grp.Key,
Total = grp.Select(t => new { t.fon, t.quot_rate }).AsQueryable()
};
var rate = (from q in db.DS
select q.fon).Distinct();
DataTable dt = new DataTable();
dt.Columns.Add("Grade");
foreach (var r in rate)
{
dt.Columns.Add(r.ToString());
}
foreach (var q in query)
{
DataRow dr = dt.NewRow();
dr["Grade"] = q.grade; //round q_grade
foreach (var t in q.Total)
{
dr[t.fon] = t.quot_rate; //count t.quot_rate
}
dt.Rows.Add(dr);
}
return dt;
You can see the comments where the numbers have to ROUND() and COUNT().
How can I define this?
EDIT:
The output is currently as follows:
Grade | AB001 | AB002 | AB003 ...
90,045| 90,045| null | null
85,590| null | 85,590| 85,590
85,450| null | 85,450| null
84,901| null | 84,901| null
and I want the result as follows:
Grade | AB001 | AB002 | AB003 ...
90 | 1 | 0 | 0
86 | 0 | 1 | 1
85 | 0 | 2 | 0
So it appears that you actually want rounding to happen inside the query, so that you can do grouping by rounded values. So first part of the question can be answered as:
Grade = Math.Round(grp.Key),
Then the counts come out naturally as:
q.Total.Count()
However it seems that you actually want counts by rate items, so I would suggest something like that for each table row:
foreach (var r in rate)
{
dr[r] = q.Total.Count(x => x.fon == r);
}

c# - Summarizing duplicate rows in datatable

I have a table and I want to sum up duplicate rows:
|name | n | |name | n |
|------+---| |------+---|
|leo | 1 | |leo | 3 |
|wayne | 1 | |wayne | 2 |
|joe | 1 | |joe | 1 |
|wayne | 1 |
|leo | 1 |
|leo | 1 |
I can delete it like this, but how to summarize?
ArrayList UniqueRecords = new ArrayList();
ArrayList DuplicateRecords = new ArrayList();
foreach (DataRow dRow in table.Rows)
{
if (UniqueRecords.Contains(dRow["name"]))
DuplicateRecords.Add(dRow);
else
UniqueRecords.Add(dRow["name"]);
}
foreach (DataRow dRow in DuplicateRecords)
{
table.Rows.Remove(dRow);
}
This is how you do it with a dictionary. Basically you create a dictionary from "name" to DataRow object and then sum up the DataRows' "n" property:
// create intermediate dictionary to group the records
Dictionary<string, DataRow> SummarizedRecords = new Dictionary<string, DataRow>();
// iterate over all records
foreach(DataRow dRow in table.Rows)
{
// if the record is in the dictionary already -> sum the "n" value
if(SummarizedRecords.ContainsKey(dRow["name"]))
{
SummarizedRecords[dRow["name"]].n += dRow["n"];
}
else
{
// otherwise just add the element
SummarizedRecords[dRow["name"]] = dRow;
}
}
// transform the dictionary back into a list for further usage
ArrayList<DataRow> summarizedList = SummarizedRecords.Values.ToList();
I think this can be done more elegantly (1 line of code) with LINQ. Let me think some more about it :)
Edit
Here is a Linq version, which however involves creating new DataRow objects, this may not be your intention - don't know:
ArrayList<DataRow> summarizedRecords = table.Rows.GroupBy(row => row["name"]) // this line groups the records by "name"
.Select(group =>
{
int sum = group.Sum(item => item["n"]); // this line sums the "n"'s of the group
DataRow newRow = new DataRow(); // create a new DataRow object
newRow["name"] = group.Key; // set the "name" (key of the group)
newRow["n"] = sum; // set the "n" to sum
return newRow; // return that new DataRow
})
.ToList(); // make the resulting enumerable a list
Thanks for your replies, another variant:
var result = from row in table.AsEnumerable()
group row by row.Field<string>("Name") into grp
select new
{
name = grp.Key,
n = grp.Count()
};

Categories

Resources