Select distinct only selects single column - c#

Based on this question:
[How can I do SELECT UNIQUE with LINQ?
I wrote the below expression to select rows with unique OrganizationID column from the dt datatabe which contains multiple columns.
var distinctRows = (from DataRow dRow in dt.Rows
select new { col1 = dRow["OrganizationID_int"] }).Distinct();
but when I check distinctRows after the expression being executed, it only has records with 1 column (col1) instead of holding the whole columns. I afraid that adding expressions like col2=... and etc, may be interpreted that I want select distinct on all these columns.
So how can I get the whole row while applying unique filter on only 1 column but not the whole columns?

I want the whole rows which satisfy that unique condition with all
columns. I want to iterate in the next step.
So you don't want to group by that field and return one of the multiple rows. You want only rows which are unique.
One way is using Enumerable.GroupBy and count the rows in each group:
var uniqueRows = dt.AsEnumerable()
.GroupBy(r => r.Field<int>("OrganizationID_int"))
.Where(g => g.Count() == 1)
.Select(g => g.First());

There is two versions of Distinct exception methods, one of them takes IEqualityComparar that can determine how you're going to distinguish different elements.
Here full example of how you can use this method:
class Item
{
public int Id {get; set;}
public string Name {get;set;}
}
class ItemComparer : IEqualityComparer<Item>
{
public bool Equals(Item x, Item y)
{
return x.Id == y.Id;
}
public int GetHashCode(Item x)
{
return x.Id;
}
}
void Main()
{
var sequence = new List<Item>()
{
new Item {Id = 1, Name = "1"},
new Item {Id = 1, Name = "2"}
};
// Using overloaded version of Distinct method!
var distinctSequence = sequence.Distinct(new ItemComparer());
// distinctSequence contains inly one Item with Id = 1
distinctSequence.Dump();
}

What you are looking for is GroupBy, followed by an aggregate function like Min, Sum, etc to select one of the row values for each column.
var distinctRows =
(from DataRow dRow in dt.Rows
group dRow by dRow["OrganizationID_int"] into g
select new { OrgId = g.Key; Col2 = g.First().Col2, Col3 = g.First().Col3 })

Use grouping with Linq to DataSet:
var distinctRows = from row in dt.AsEnumerable()
group row by new {
col1 = row.Field<int>("OrganizationID_int")
// other columns here
} into g
select g.First();

Have a look at MoreLinq's DistinctBy method, with which you can phrase your query like so:
dt.Rows.DistinctBy(dRow => dRow["OrganizationID_int"])

Related

LINQ: select specific value in a datatable column

In table I have 4 Columns GroupName, Display, Value and ID
How can I just show a specific data in display. I only want to show some of the groupNames Data
for example I only want to show Groupname = company and display = Forbes
Here's my linq
sample = (from c in smsDashboardDBContext.CodeDefinitions
orderby c.Display ascending
select new CodeDefinitionDTO
{
GroupName = c.GroupName,
Display = c.Display,
Value = c.Value,
Id = c.Id
}).ToList();
You can add a where statement in the query.
where c.GroupName == "company" && c.Display == "Forbes"
I only want to show some of the groupNames Data for example I only want to show Groupname = company and display = Forbes
Before the ToList, use a Where to keep only those items that you want to show:
var company = ...
var forbes = ...
var result = smsDashboardDBContext.CodeDefinitions
.OrderBy(codeDefinition => codeDefintion.Display)
.Select(codeDefinition => new CodeDefinitionDTO
{
Id = codeDefinition.Id,
GroupName = codeDefinition.GroupName,
Display = codeDefinition.Display,
Value = codeDefinition.Value,
})
.Where(codeDefinition => codeDefition.GroupName == company
&& codeDefintion.Display == forbes);
In words:
Order all codeDefinitions that are in the table of CodeDefintions by ascending value of property codeDefintion.Display.
From every codeDefinition in this ordered sequence make one new CodeDefinitionDTO with the following properties filled: Id, GroupName, Display, Value
Frome every codeDefintion in this sequence of CodeDefinitionDTOs, keep only those codeDefinitions that have a value for property GroupName that equals company and a value for property Display that equals forbes.
There is room for improvement!
Suppose your table has one million elements, and after the Where, only five elements are left. Then you will have sorted almost one million elements for nothing. Consider to first do the Where, then the Order and finally a Select.
In LINQ, try to do aWhere as soon as possible: all following statements will have to work on less items
In LINQ, try to do a Select as late as possible, preferrably just before the ToList / FirstOrDefault / ... This way the Select has to be done for as few elements as possible
So first the Where, then the OrderBy, then the Select, and finally the ToList / FirstOrDefault, etc:
var result = smsDashboardDBContext.CodeDefinitions
.Where(codeDefinition => ...);
.OrderBy(codeDefinition => codeDefintion.Display)
.Select(codeDefinition => new CodeDefinitionDTO
{
...
});

convert rows to column in entity framwork

how can i convert rows to column in entity framework!?
i have a result like this:
and i want this result:
my entity code i this :
(from loanPerson in context.LoanPersons.AsParallel()
join warranter in context.Warranters.AsParallel() on loanPerson.Id equals warranter.LoanPersonId
where loanPerson.Id == 84829
select new
{
loanPersonId = loanPerson.Id,
waranterId = warranter.WarranterPersonID,
}).ToList();
and number of the row always less than 3 and i want to have 3 column.
please let me know your answer.
tanks.
This query will return the only one row, where waranterIds will contain, at this particular case, three WarranterPersonID values, also this field is of List<int> type, because it's quantity not known at compile time:
var answer = (from loanPerson in context.LoanPersons.Where(x => x.Id == 84829)
join warranter in context.Warranters
on loanPerson.Id equals warranter.LoanPersonId
group warranter by loanPerson.Id into sub
select new
{
loanPersonId = sub.Key,
waranterIds = sub.Select(x => x.LoanPersonId).ToList()
//if you sure, that quantity equals 3,
//you can write this code instead of waranterIds:
//zamen1 = sub.Select(x => x.LoanPersonId).First(),
//zamen2 = sub.Select(x => x.LoanPersonId).Skip(1).First(),
//zamen3 = sub.Select(x => x.LoanPersonId).Skip(2).First()
}).ToList();

Query Row count from Grouped EF query

I have this query to group the levels of a particular row in EF
var awards = from a in context.Awards
where a.TWID == employee.TWID
group a by a.AwardLevel;
This gives me the awards for each level (1-4) what I'm trying to figure out is how to extract the count from the awards for a specific level.
ie: level1.count,level2.count etc.
I know this should be some simple lambda expression or something but I just can't get it.
UPDATE What I'm looking for is a way NOT to write 4 different queries. For example:
var level1 = awards.Level[0]
var level2 = awards.Level[1]
Try:
var awards = from a in context.Awards
where a.TWID == employee.TWID
group a by a.AwardLevel into award
select new
{
AwardLevel = award.Key,
Count = award.Count()
};
Update based on updated question:
var awards = (from a in context.Awards
where a.TWID == employee.TWID
group a by a.AwardLevel into award
select new
{
AwardLevel = award.Key,
Count = award.Count()
}).ToDictionary( t => t.AwardLevel, t => t.Count );

LINQ: Add RowNumber Column

How can the query below be modified to include a column for row number (ie: one-based index of results)?
var myResult = from currRow in someTable
where currRow.someCategory == someCategoryValue
orderby currRow.createdDate descending
select currRow;
EDIT1: I'm looking for the results to be {idx, col1, col2...col-n} not {idx, row}.
EDIT2: The row number should correspond to result rows not the table rows.
EDIT3: I DataBind these results to a GridView. My goal was to add a row number column to the GridView. Perhaps a different approach would be better.
Use the method-syntax where Enumerable.Select has an overload with the index:
var myResult = someTable.Select((r, i) => new { Row = r, Index = i })
.Where(x => x.Row.someCategory == someCategoryValue)
.OrderByDescending(x => x.Row.createdDate);
Note that this approach presumes that you want the original index of the row in the table and not in the filtered result since i select the index before i filter with Where.
EDIT: I'm looking for the results to be {idx, col1, col2...col-n} not
{idx, row}. The row number should correspond to result rows not
the table rows.
Then select the anonymous type with all columns you need:
var myResult = someTable.Where(r => r.someCategory == someCategoryValue)
.OrderByDescending(r => r.createdDate)
.Select((r, i) => new { idx = i, col1 = r.col1, col2 = r.col2, ...col-n = r.ColN });
Use this Select method:
Projects each element of a sequence into a new form by incorporating the element's index.
Example:
var myResult = someTable.Where(currRow => currRow.someCategory == someCategoryValue)
.OrderByDescending(currRow => currRow.createdDate)
.Select((currRow, index) => new {Row = currRow, Index = index + 1});
In response to your edit:
If you want a DataTable as result, you can go the non-Linq way by simply using a DataView and add a additional column afterwards.
someTable.DefaultView.RowFilter = String.Format("someCategory = '{0}'", someCategoryValue);
someTable.DefaultView.Sort = "createdDate";
var resultTable = someTable.DefaultView.ToTable();
resultTable.Columns.Add("Number", typeof(int));
int i = 0;
foreach (DataRow row in resultTable.Rows)
row["Number"] = ++i;
what about?
int i;
var myResult = from currRow in someTable
where currRow.someCategory == someCategoryValue
orderby currRow.createdDate descending
select new {Record = i++, currRow};
Just for fun, here's an alternative to Select with two arguments:
var resultsWithIndexes = myResult.Zip(Enumerable.Range(1, int.MaxValue - 1),
(o, i) => new { Index = i, Result = o });
According to you edit 1. NO, YOU CAN'T Linq returns the table as it is. You can build each column, but you lose the power of mapped entities.
This has been asked multiple times before: How do you add an index field to Linq results
There is no straightforward way if want to keep a flat list of columns (i.e. OP's Edit2) and also want a generic solution that works with any IEnumerable without requiring you to list out the set of expected columns.
However, there is a roundabout way to kinda go about it which is to dump the query results into a DataTable using the ToDataTable() method from here and then add a RowNumber column to that table.
var table = query.ToList().ToDataTable();
table.Columns.Add("RowNum", typeof(int));
int i = 0;
foreach (DataRow row in table.Rows)
row["RowNum"] = ++i;
This would likely cause performance issues with large datasets but it's not insanely slow either. On my machine a dataset with ~6500 rows took 33ms to process.
If your original query returned an anonymous type, then that type definition will get lost in the conversion so you'll lose the static typing on the column names of the resulting IEnumerable when you call table.AsEnumerable(). In other words, instead of being able to write something like table.AsEnumerable().First().RowNum you instead have to write table.AsEnumerable().First()["RowNum"]
However, if you don't care about performance and really want your static typing back, then you can use JSON.NET to convert the DataTable to a json string and then back to a list based on the anonymous type from the original query result. This method requires a placeholder RowNum field to be present in the original query results.
var query = (from currRow in someTable
where currRow.someCategory == someCategoryValue
orderby currRow.createdDate descending
select new { currRow.someCategory, currRow.createdDate, RowNum = -1 }).ToList();
var table = query.ToDataTable();
//Placeholder RowNum column has to already exist in query results
//So not adding a new column, but merely populating it
int i = 0;
foreach (DataRow row in table.Rows)
row["RowNum"] = ++i;
string json = JsonConvert.SerializeObject(table);
var staticallyTypedList = JsonConvert.DeserializeAnonymousType(json, query);
Console.WriteLine(staticallyTypedList.First().RowNum);
This added about 120ms to the processing time for my 6500 item dataset.
It's crazy, but it works.
I know I'm late to the party, but I wanted to show what worked for me.
I have a list of objects, and the object has an integer property on it for "row number"... or in this case, "Sequence Number". This is what I did to populate that field:
myListOfObjects = myListOfObjects.Select((o, i) => { o.SequenceNumber = i; return o; }).ToList();
I was surprised to see that this worked.
This one helped me in my case - Excel sheet extraction. anonymous type
var UploadItemList = ItemMaster.Worksheet().AsEnumerable().Select((x, index) => new
{
Code = x["Code"].Value == null ? "" : x["Code"].Value.ToString().Trim(),
Description = x["Description"].Value == null ? "" : x["Description"].Value.ToString().Trim(),
Unit = x["Unit"].Value == null ? "" : x["Unit"].Value.ToString().Trim(),
Quantity = x["Quantity"].Value == null ? "" : x["Quantity"].Value.ToString().Trim(),
Rate = x["Rate"].Value == null ? "" : x["Rate"].Value.ToString().Trim(),
Amount = x["Amount"].Value == null ? "" : x["Amount"].Value.ToString().Trim(),
RowNumber = index+1
}).ToList();
int Lc = 1;
var Lst = LstItemGrid.GroupBy(item => item.CategoryName)
.Select(group => new { CategoryName = group.Key, Items = group.ToList() ,RowIndex= Lc++ })
.ToList();

c# linq filter plus concat

I have the following code:
var data= from row in table.AsEnumerable()
group row by row.Field<string>("id") into g
select new { Id= g.Key };
I would like to add 1 more element to the data variable. How would I do it? (is there something like a data.Concat(1) etc
If you want to add an additional Id you can indeed concat it:
data = data.Concat( new [] { new { Id= "1" } });
This works because anonymous types that have the same fields in the same order are compiled down to the same type.
You can return the LINQ result as a List<string>.
Generic lists can Add more itens easily.
var data = (from row in table.AsEnumerable()
group row by row.Field<string>("id") into g
select new { Id = g.Key }).ToList();
data.Add(new { Id = "X" });
This way you will not need to declare another variable to hold the Enumerable with the new item (since an Enumerable is imutable and can't add new items to itself).
EDIT:
Like pointed, changing the Enumerable<T> to List<T> will put and hold all the elements on the memory, wich isn't a good performance approach.
To stay with the Enumerable<T>, you can do:
data = data.Concat(new [] { new { Id = "X" } });
Because a Enumerable<Anonymous> can be placed inside itself.
I don't know how with query syntax, but you can use Concat with a single value by creating a new array of values with a single item:
IEnumerable<int> data = GetData()
.Concat(new[] { "5" });
The problem with doing this simply is that your data is an IEnumerable<AnonymousType>, which you can't simply new up, and I don't think new anonymous types are compatible with each other. (Edit: According to BrokenGlass, they are compatible. You can try his solution instead).
If they aren't compatible, you could concat the item before your Select clause, but again, how do you create an item of that type.
The solution would probably be to select to an IEnumerable<string> first, concat, then re-select into your anonymous type:
var data = (from row in table.AsEnumerable()
group row by row.Field<string>("id") into g
select g.Key)
.Concat(new[] { "5" })
.Select(k => new { Id = key });
Or to create a new named structure for your result, and concatenate one of those:
var data = from row in table.AsEnumerable()
group row by row.Field<string>("id") into g
select new MyCustomResult() { Id = g.Key };
data = data.Concat(new MyCustomResult() { Id = "5" });
You would need to change from an anonymous type to a known type and then add that new element.
// ...
select new MyResultClass { Id = g.Key };
data.Add(new MyResultClass { Id = 4 });

Categories