I have a datatable with the following information:
365.00
370.00
369.59
365.00
365.00 -> match with previous item
365.00 -> match with previous item
I only need to remove the next matched items, like this:
365.00
370.00
369.59
365.00
I tried:
(from articlespricehistory in dt.AsEnumerable()
select new
{
articlepricehistory_cost = articlespricehistory.Field<Double>("articlepricehistory_cost")
})
.DistinctBy(i => i.articlepricehistory_cost)
.ToList();
Result:
365.00
370.00
369.59
Any ideas?
Another approach:
public static IEnumerable<T> MyDistinct<T>(this IEnumerable<T> items)
{
T previous = default(T);
bool first = true;
foreach(T item in items)
{
if (first || !Equals(previous, item))
{
first = false;
previous = item;
yield return item;
}
}
}
Or, as requested, with a selector
public static IEnumerable<T> MyDistinct<T, U>(this IEnumerable<T> items, Func<T, U> selector)
{
U previous = default(U);
bool first = true;
foreach(T item in items)
{
U current = selector(item);
if (first || !Equals(previous, current))
{
first = false;
previous = current;
yield return item;
}
}
}
Here's a neat LINQ solution for u
var list = (dt as Enumerable);
var numbers = list.TakeWhile((currentItem, index) => currentItem != list.ElementAtOrDefault(index - 1));
Keep in mind if u have 0 as the first element it will be ommitted from the new list since ElementAtOrDefault will return 0 in the first iteration of the while loop (index of -1), thus evaluating the expression to false. A simple if statement can help you avoid this.
Here's an idea I have not actually tried
Do a Skip(1) on the query to produce a second query.
Now append to the second query any element not equal to the last element in the first query, to produce a third query.
Now zip join the first and third queries together to form a set of pairs; this is the fourth query.
Now construct a fifth query that filters out pairs that have identical elements from the fourth query.
Finally, construct a sixth query that selects the first element of each pair from the fifth query.
The sixth query is the data set you want.
The problem in your query is that you are using .DistinctBy() which will return distinct results only. So if 365.00 appeared anywhere, it won't show up in the returned list again.
var differentPreviousList = new List<double>();
var itemPriceList = dt.ToList();
differentPreviousList.Add(itemPriceList[0]);
for (var index = 1; index < itemPriceList.Count; index++)
{
if (itemPriceList[index - 1] == itemPriceList[index]) continue;
differentPriceList.Add(itemPriceList[index]);
}
It may not be an elegant solution but I would just parse a bare query..here is how.
Run a lambda query to get all the original results without trying to filter out DistinctBy.
Create an object of a single query result of the type you initially queried for.
Initialize a new list for foreach parse results.
Do a for each loop for each result.
The first if section should be if(object above loop is null).
IF is true add item to list
ELSE if check to see if value of item is same as the last iteration.
Store foreach iteration object to the object declared before the loop.
Rinse and repeat, and the result is no duplicate objects found in a row in the loop will be stored in the list resulting in what you wanted.
I think you have to use a temporary value to check if the next value matches the current value or not.
double temporary = -1; // temp value for checking
List<double> results = new List<double>(); // list to store results
(from articlespricehistory in dt.AsEnumerable()
select new
{
articlepricehistory_cost = articlespricehistory.Field<Double>("articlepricehistory_cost")
})
.Select(a => a.articlepricehistory_cost)
.ToList()
.ForEach(cost =>
{
if (temporary != cost) { results.Add(cost); }
temporary = cost;
});
foreach (var result in results)
{
Console.WriteLine(result);
}
After ForEach method is equivalent to the following.
foreach (var cost in costs)
{
if (temporary != cost)
{
results.Add(cost);
Console.WriteLine(cost);
}
temporary = cost;
}
Related
So suppose we have a parking(represented as a dictionary<int,bool> :
Every parking lot has its id and a boolean(free,filled).
This way:
Dictionary<int,bool> parking..
parking[0]= true // means that the first parking lot is free
My question is i want to get the all sublist of consecutive elements that matchs in a condition : parking-lot is free.
First i can get elements that fits in this condition easy:
parking.Where(X => X.Value).Select(x => x.Key).ToList();
But then using linq operations i dont know how to get the first generated list that matchs in.
Can i do this without thousand of foreach-while loops checking iterating one by one, is there a easier way with linq?
This method gets a list of consecutive free parking lots
data:
0-free,
1-free,
2-filled ,
3-free
The results will be two lists:
First One will contain => 0 ,1
Second One will contain=> 3
These are the list of consecutive of parking lots that are free.
public List<List<int>> ConsecutiveParkingLotFree(int numberOfConsecutive){}
You can always write your own helper function to do things like this. For example
public static IEnumerable<List<T>> GroupSequential<T, TKey>(
this IEnumerable<T> self,
Func<T, bool> condition)
{
var list = new List<T>();
using var enumerator = self.GetEnumerator();
if (enumerator.MoveNext())
{
var current = enumerator.Current;
var oldValue = condition(current);
if (oldValue)
{
list.Add(current);
}
while (enumerator.MoveNext())
{
current = enumerator.Current;
var newValue = condition(current);
if (newValue)
{
list.Add(current);
}
else if (oldValue)
{
yield return list;
list = new List<T>();
}
oldValue = newValue;
}
if (list.Count > 0)
{
yield return list;
}
}
}
This will put all the items with a true-value in a list. When a true->false transition is encountered the list is returned and recreated. I would expect that there are more compact ways to write functions like this, but it should do the job.
You can apply GroupWhile solution here.
parking.Where(X => X.Value)
.Select(x => x.Key)
.GroupWhile((x, y) => y - x == 1)
.ToList()
I have a DB used for a production line. It has an Orders table, and Ordertracker table, an Item table, and an Itemtracker table.
Both Orders and Items have many-to-many relationships with status. The tracker tables resolves these relationships in such a way that an item can have multiple entries in the tracker - each with a particular status.
I tried to upload a picture of the tables to make things clearer but alas, I don't have enough points yet :C
I need to find items whose last status in the Itemtracker table meets a condition, either '3' or '0'.
I then need to get the first one of these items.
The steps I am using to accomplish this are as follows:
Get all the Orders which have a certain status.
Get all the Items in that Order.
Get all the Items whose last status was = 0 or 3.
Get the first of these items.
My code is as follows:
public ITEM GetFirstItemFailedOrNotInProductionFromCurrentOrder()
{
var firstOrder = GetFirstOrderInProductionAndNotCompleted();
var items = ERPContext.ITEM.Where(i => i.OrderID == firstOrder.OrderID) as IQueryable<ITEM>;
if (CheckStatusOfItems(items) != null)
{
var nextItem = CheckStatusOfItems(items);
return nextItem ;
}
else
{
return null;
}
}
private ITEM CheckStatusOfItems(IQueryable<ITEM> items)
{
List<ITEM> listOfItemsToProduce = new List<ITEM>();
foreach (ITEM item in items.ToList())
{
var lastStatusOfItem = ERPContext.ITEMTRACKER.Where(it => it.ItemID == item.ItemID)
.OrderByDescending(it => it.ItemTrackerID).FirstOrDefault();
if (lastStatusOfItem.ItemStatus == (int)ItemStatus.Failed || lastStatusOfItem.ItemStatus == (int)ItemStatus.Confirmed)
{
listOfItemsToProduce.Add(item);
}
}
return listOfItemsToProduce.FirstOrDefault();
}
Now, this all works fine and returns what I need but I'm aware that this might not be the best approach. As it is now my IQueryable collection of items will never contain more than 6 items - but if it could grow larger, then calling ToList() on the IQueryable and iterating over the results in-memory would probably not be a good idea.
Is there a better way to iterate through the IQueryable items to fetch out the items that have a certain status as their latest status without calling ToList() and foreaching through the results?
Any advice would be much appreciated.
Using LINQ query syntax, you can build declaratively a single query pretty much the same way you wrote the imperative iteration. foreach translates to from, var to let and if to where:
private ITEM CheckStatusOfItems(IQueryable<ITEM> items)
{
var query =
from item in items
let lastStatusOfItem = ERPContext.ITEMTRACKER
.Where(it => it.ItemID == item.ItemID)
.OrderByDescending(it => it.ItemTrackerID)
.FirstOrDefault()
where (lastStatusOfItem.ItemStatus == (int)ItemStatus.Failed || lastStatusOfItem.ItemStatus == (int)ItemStatus.Confirmed)
select item;
return query.FirstOrDefault();
}
or alternatively using from instead of let and Take(1) instead of FirstOrDefault():
private ITEM CheckStatusOfItems(IQueryable<ITEM> items)
{
var query =
from item in items
from lastStatusOfItem in ERPContext.ITEMTRACKER
.Where(it => it.ItemID == item.ItemID)
.OrderByDescending(it => it.ItemTrackerID)
.Take(1)
where (lastStatusOfItem.ItemStatus == (int)ItemStatus.Failed || lastStatusOfItem.ItemStatus == (int)ItemStatus.Confirmed)
select item;
return query.FirstOrDefault();
}
Below is the code:
string[] values = Acode.Split(',');
IEnumerable<Test> tst = null;
foreach (string a in values)
{
if (tst== null)
tst = entities.Test.Where(t=> (t.TCode == Convert.ToInt16(a)));
else
tst.Concat(entities.Test.Where(g => (g.TCode == Convert.ToInt16(a))));
}
return tst.ToList();
I am not able to get all the records in tst, it is giving me records only for the last value in array.
So if my array contains 1,2,3,4 I am getting records only for the 4. Whereas i need all the result for 1,2,3 and 4 get appended in tst.
Any help will be appreciated.
Concat doesn't modify anything - it returns a new sequence, which you're currently ignoring.
However, rather than using Concat, you should just use SelectMany to flatten the sequence:
string[] values = Acode.Split(',');
return values.SelectMany(a => entities.Test.Where(t => t.TCode == Convert.ToInt16(a)))
.ToList();
Or more efficiently, convert values into a List<short> and then you can do one query:
List<short> values = Acode.Split(',').Select(x => short.Parse(x)).ToList();
return entities.Test.Where(t => values.Contains(t.TCode)).ToList();
That is because Concat will return a new instance of your enumerable.
Either use in your else :
tst = tst.Concat(...)
Or Change your Enumerable into list from the beginning :
string[] values = Acode.Split(',');
List<Test> tst= new List<Test>;
foreach (string a in values)
{
tst.AddRange(entities.Test.Where(g => (g.TCode == Convert.ToInt16(a))));
}
return tst;
I have a list of items and I want to create two ways to sort them, Alphabetically and Last Modified.
Here's what I did:
// Alphabetically
tableItems = tableItems.OrderBy (MyTableItem => MyTableItem.ItemName).ToList();
reloadTable(tableItems);
// Last Modified
tableItems = tableItems.OrderBy (MyTableItem => MyTableItem.Timestamp).ToList();
reloadTable(tableItems);
and this works perfectly fine.
My problem is I want this happen to all items in the list except for one.
This one item will always be constant and I want to make sure it's ALWAYS on the top of the list.
What would I need to do for that?
if it matters, c# is the lang.
Thank you for your time.
tableItems = tableItems.OrderBy(i => i.ItemName != "yourexceptitem").ThenBy(i => i.Timestamp).ToList();
EDIT:
If you want to sort the itemname except one, do like this,
tableItems = tableItems.OrderBy(i => i.ItemName != "TestSubject3").ToList();
Other, generic solution:
public static IEnumerable<T> OrderByExcept<T>(
this IEnumerable<T> source,
Predicate<T> exceptPredicate,
Func<IEnumerable<T>, IOrderedEnumerable<T>> projection)
{
var rest = new List<T>();
using (var enumerator = source.GetEnumerator())
{
while (enumerator.MoveNext())
{
if (exceptPredicate(enumerator.Current))
{
yield return enumerator.Current;
}
else
{
rest.Add(enumerator.Current);
}
}
}
foreach (var elem in projection(rest))
{
yield return elem;
}
}
Usage:
tableItems = tableItems.OrderByExcept(
item => item.ItemName == "TestSubject3",
items => items.OrderBy(MyTableItem => MyTableItem.ItemName)
.ThenBy(MyTableItem => MyTableItem.TimeStamp))
.ToList();
Items that meets predicate will always be on the top of list, to the rest of elements projection will be applied.
I have a list of RowData items (a custom class).
A RowData item contains a list of CellData items (another custom class).
Currently, the only way I can access each individual CellData item is through the RowData's this[int index], which means I'd have to create a loop for each search.
What I want is to create a method that will return all of the CellData items for a given index value:
private List<RowData> rowList;
public IEnumerable<CellData> Cells(int index) {
foreach (var row in rowList) {
if (row.ID == index) {
return row.AsEnumerable<CellData>();
}
}
return null;
}
I don't like returning NULL, and I'd like to know how to accomplish this task using some LINQ techniques (either Query or Method syntax).
To be able to use LINQ syntax for getting cells collection from row, your RowData need to be enumerable, i.e. implement IEnumerable interface.
By .AsEnumerable<CellData>() in your example, I assume your RowData already implements it and enumerating RowData returns collection of CellData objects.
If not, you need to implement IEnumerable in RowData on your own. If your this[int index] accessor reads the data from a collection, you can just return that collection's GetEnumerator.
And then, you'll be able to fetch CellData collection from RowData collection using LINQ queries, i.e. like this:
var cells = rowList.Where(x => x.ID == index).SelectMany(x => x);
Assuming only one row is returned given an index:
public IEnumerable<CellData> Cells(int index)
{
var foundRow = rowList.FirstOrDefault(row => row.ID == index);
//if no row was found, foundRow will be null
if(foundRow != null)
return foundRow.AsEnumerable<CellData>();
else
return new CellData[]; //return empty cells IEnumerable. Could also be new List<CellData>()
}
}
This code assumes foundRow.AsEnumerable<CellData>() returns a IEnumerable<CellData>.
You have to check for nullity, because row at a given index might nor be found.
This will achieve the same result.
var resultsList = from x in rowList
where x.ID == index
select x;
return resultsList;
public IEnumerable<CellData> Cells(int index){
var cells = (from r on rowList
where r.ID == index
select r.Cells).FirstOrDefault();
return cells;
}
What about:
List<CellData> cells = rowList.Where(x => x.Index == index).SelectMany(x => x.Cells).ToList();