Multiple OfType Linq? - c#

I have a linq query that selects all textboxes in a placeholder and adds them to a list using a struct. I need to expand this functionality to also take the selectedvalue of a DropDownList I am pretty sure I am doing this wrong, because when I debug the method the lists count is 0.
My own guess is that declaring 2 OfType<>() is wrong, but I am pretty new to linq and I have no idea of how else to do it.
Any help would be awesome! Thanks in advance.
Here's what I have so far:
public struct content
{
public string name;
public string memberNo;
public int points;
public string carclass;
}
List<content> rows = new List<content>();
protected void LinkButton_Submit_Attendees_Click(object sender, EventArgs e)
{
List<content> rows = PlaceHolder_ForEntries.Controls.OfType<TextBox>().OfType<DropDownList>()
.Select(txt => new
{
Txt = txt,
Number = new String(txt.ID.SkipWhile(c => !Char.IsDigit(c)).ToArray())
})
.GroupBy(x => x.Number)
.Select(g => new content
{
carclass = g.First(x => x.Txt.ID.StartsWith("DropDownlist_CarClass")).Txt.SelectedValue,
name = g.First(x => x.Txt.ID.StartsWith("TextBox_Name")).Txt.Text,
memberNo = g.First(x => x.Txt.ID.StartsWith("TextBox_MemberNo")).Txt.Text,
points = int.Parse(g.First(x => x.Txt.ID.StartsWith("TextBox_Points")).Txt.Text)
})
.ToList();
}
Here's the method that creates the controls.
protected void createcontrols()
{
int count = 0;
if (ViewState["count"] != null)
{
count = (int)ViewState["count"];
}
while (PlaceHolder_ForEntries.Controls.Count < count)
{
TextBox TextBox_Name = new TextBox();
TextBox TextBox_MemberNo = new TextBox();
TextBox TextBox_Points = new TextBox();
DropDownList DropDownList_CarClass = new DropDownList();
DropDownList_CarClass.Items.Add("Car1");
...
DropDownList_CarClass.Items.Add("Car2");
TextBox_Name.Attributes.Add("placeholder", "Navn");
TextBox_Name.ID = "TextBox_Name" + PlaceHolder_ForEntries.Controls.Count.ToString();
TextBox_Name.CssClass = "input-small";
TextBox_MemberNo.Attributes.Add("placeholder", "Medlemsnr.");
TextBox_MemberNo.ID = "TextBox_MemberNo" + PlaceHolder_ForEntries.Controls.Count.ToString();
TextBox_MemberNo.CssClass = "input-small";
TextBox_Points.Attributes.Add("placeholder", "Point");
TextBox_Points.ID = "TextBox_Points" + PlaceHolder_ForEntries.Controls.Count.ToString();
TextBox_Points.CssClass = "input-small";
PlaceHolder_ForEntries.Controls.Add(TextBox_Name);
PlaceHolder_ForEntries.Controls.Add(TextBox_MemberNo);
PlaceHolder_ForEntries.Controls.Add(DropDownList_CarClass);
PlaceHolder_ForEntries.Controls.Add(TextBox_Points);
PlaceHolder_ForEntries.Controls.Add(new LiteralControl("<br />"));
}
}

you can use the Where and check if the instance of object is of type!
List<content> rows = PlaceHolder_ForEntries.Controls.Cast<Control>().Where(c => c is TextBox || c is DropDownList)
.Select(txt => new
{
Txt = txt,
Number = new String(txt.ID.SkipWhile(c => !Char.IsDigit(c)).ToArray())
})
.GroupBy(x => x.Number)
.Select(g => new content
{
carclass = g.First(x => x.Txt.ID.StartsWith("DropDownlist_CarClass")).Txt.SelectedValue,
name = g.First(x => x.Txt.ID.StartsWith("TextBox_Name")).Txt.Text,
memberNo = g.First(x => x.Txt.ID.StartsWith("TextBox_MemberNo")).Txt.Text,
points = int.Parse(g.First(x => x.Txt.ID.StartsWith("TextBox_Points")).Txt.Text)
})
.ToList();

AppDeveloper is right. OfType<T> filters out all objects of types other than T; so by filtering twice, you effectively eliminate all objects in the list.
If you wanted to wrap this logic (filtering all but two types from a list) into something reusable, nothing's stopping you from implementing your own extension method:
using System.Collections;
public static class EnumerableExtensions
{
public static IEnumerable OfType<T1, T2>(this IEnumerable source)
{
foreach (object item in source)
{
if (item is T1 || item is T2)
{
yield return item;
}
}
}
}
Including the above class in your project will allow you to write code like this in your application:
var textBoxesAndDropDowns = controls.OfType<TextBox, DropDownList>();
To learn more about extension methods, see the MSDN article on the subject.
Note that since the extension method above "lets in" two different types, the result is still a non-generic IEnumerable sequence. If you wanted to treat the result as a generic sequence (e.g., an IEnumerable<Control>), I would recommend using the Cast<T> extension method:
var filteredControls = controls.OfType<TextBox, DropDownList>().Cast<Control>();

I haven't read the question thoroughly, but from what the title implies, you can achieve the behavior with:
var collection = new object[] { 5, "4545", 'd', 54.5 , 576 };
var allowedTypes = new[] { typeof(string), typeof(int) };
var result = collection
.Where(item => allowedTypes.Contains(item.GetType()));
See it in action here.

Took #Shimmys answer for an extension method:
/// <param name="wantedTypes">--- Sample: --- new Type[] { typeof(Label), typeof(Button) }</param>
public static IEnumerable OfTypes(this IEnumerable collection, Type[] wantedTypes)
{
if (wantedTypes == null)
return null;
else
return collection.Cast<object>().Where(element => wantedTypes.Contains(element.GetType()));
}
Usage:
// List of 3 different controls
List<object> controls = new List<object>(new object[] { new Label(), new Button(), new TextBox() });
// Get all labels and buttons
var labelsAndButtons = controls.OfTypes(new Type[] { typeof(Label), typeof(Button) });

Your problem is in the OfType<>().OfType<>() you filter twice with different types

Related

Compare two lists to determine if both lists contain same items using lambda predicates

I am trying to return a boolean value if my object list contains all the types in a list of types in either vb.net or C#. I am struggling in writing a lambda expression to accomplish this. Can this be done using lambda predicates? I know it can be done easily with a for each loop.
vb.net
Public Class Widget
Public wobbly As String
Public sprocket As String
Public bearing As String
End Class
Public Sub test()
Dim wList As New List(Of Widget)
wList.Add(New Widget() With {.bearing = "xType", .sprocket = "spring", .wobbly = "99"})
wList.Add(New Widget() With {.bearing = "yType", .sprocket = "sprung", .wobbly = "45"})
wList.Add(New Widget() With {.bearing = "zType", .sprocket = "straight", .wobbly = "17"})
Dim typeList As New List(Of String) From {"xType", "yType", "zType"}
Dim containsAllTypes As Boolean = wList.TrueForAll(Function(a) a.bearing.Equals(typeList.Where(Function(b) b = a.bearing)))
Debug.WriteLine(containsAllTypes.ToString)
End Sub
C#
public class Widget
{
public string wobbly;
public string sprocket;
public string bearing;
}
public void test()
{
List<Widget> wList = new List<Widget>();
wList.Add(new Widget {
bearing = "xType",
sprocket = "spring",
wobbly = "99"
});
wList.Add(new Widget {
bearing = "yType",
sprocket = "sprung",
wobbly = "45"
});
wList.Add(new Widget {
bearing = "zType",
sprocket = "straight",
wobbly = "17"
});
List<string> typeList = new List<string> {
"xType",
"yType",
"zType"
};
bool containsAllTypes = wList.TrueForAll(a => a.bearing.Equals(typeList.Where(b => b == a.bearing)));
Debug.WriteLine(containsAllTypes.ToString());
}
EDIT, thanks for all the quick answers, I see there are a few ways to do this, and now have a better understanding of what is happening in the expression.
Try var containsAllTypes = typeList.All(x => wList.Select(x => x.bearing).Contains(x))
var containsAll = typeList.All(type =>
wList.Any(widget => widget.bearing.Equals(type)));
Translated, it is true for all types in typeList that any (at least one) widget in the list has that type.
I believe what you want is the following:
bool containsAllTypes1 = wList.TrueForAll(a => null != typeList.Find(b => b == a.bearing));
You can also use System.Linq as follows:
bool containsAllTypes2 = wList.All(a => typeList.Any(b => b == a.bearing));
Dim containsAllTypes As Boolean = wList.All(Function(a) typeList.Any(Function(b) b = a.bearing))
For each value in wList, it checks to see if any values in typeList match the wList bearing value.
The shorter is
containsAllTypes = wList.Where(x => typeList.Contains(x.bearing)).Count() == typeList.Count;
or
containsAllTypes = wList.Select(x => x.bearing).Except(typeList).Count() == 0;
or
containsAllTypes = wList.Select(x => x.bearing).Intersect(typeList).Count() == typeList.Count;
You can use Intersect to check if both list has same values. Try this code
var hasAll = wList
.Select(w => w.bearing)
.Distinct()
.Intersect(typeList)
.Count() == typeList.Count;
If you need to have hasAll == true only if all types in wList occured once, remove call to Distinct

How do I conditionally update List<Object> items using LINQ

How do I conditionally update List<object> items using LINQ, I have managed to get a way to update all items in List<object> using this syntax:
var updatedList = sourceList.Select( x => { x.PropertyA = NewValue; return x; }).ToList();
var updatedList = sourceList.Select( x => {return x = x.PropertyA == SomeValue ? x1 : x;}).ToList();
You can either project it to a new collection using the .Select, or affect the original list using the .ForEach extension method. Both are shown below, given an arbitrary condition.
class Program
{
static void Main(string[] args)
{
List<testClass> ogList = new List<testClass>();
bool testCondition = true;
//use the select to update and project to new collection
var updated =
ogList
.Select(x =>
{
if (testCondition)
x.testProp = 1;
return x;
}).ToList();
//use the foreach to update original collection
ogList.ForEach(x =>
{
if (testCondition)
x.testProp = 1;
});
}
}
public class testClass
{
public int testProp { get; set; }
}
You can use this code:
sourceList.Where(w => w.CheckProperty == valueofProperty).Select(s => {
s.PropertyA = valueofPropertyA ;
s.PropertyB = valueofPropertyB;
return s;
}).ToList();
Hope this answer will help you.
So here I creating objects of my database, list of my db_table and table.
private DB dbs = new DB();
List<tab1> lst = new List<tab1>();
tab1 tabobj = new tab1();
here you will get the list of data in your table
lst = dbs.tab1.ToList();
````````````
updating the table
```````````
tabobj.c1 = "value";
tabobj.c2 = "value";
adding updated columns to table
dbs.tab1.Add(tabobj);
save changes here
dbs.SaveChanges();
data of updated table
lst = dbs.tab1.ToList();

How to assign "var" inside if statement

I need to do this:
var productsLocation = response.blah blah; //with linq query
var item; // even var item = null; //not valid
if(condition){
item = productsLocation.linq query
} else {
item = productsLocation.differentquery
}
var group = item.query;
Is this possible? If yes, how?
EDIT: here is my exact code:
var productLocation = response.productLocation.Select(p => ProductLocationStaticClass.DtoToModel(p));
var items;
if (condition)
{
items = productLocation.Select(s => new ProductClass(s)).Where(s => categories.Contains(s.CategoryName));
} else {
items = productLocation.Select(s => new ProductClass(s)).Where(s => categories.Contains(s.CategoryName) && stocks.Contains(s.Barcode));
}
If you look closely at the logic, you notice you don't actually even need the if block. The whole thing can be written in one expression as follows:
var items = productLocation
.Select(s => new ProductClass(s))
.Where(s => categories.Contains(s.CategoryName) && (condition || stocks.Contains(s.Barcode)));
First of all get your response variable type, then initialize 'item' variable as IEnumarable where T is same as response variable type
var productsLocation = response.productLocation.Select(p => ProductLocationStaticClass.DtoToModel(p));
IEnumerable<ProductClass> item;
if (condition)
{
items = productLocation.Select(s => new ProductClass(s)).Where(s => categories.Contains(s.CategoryName));
}
else
{
items = productLocation.Select(s => new ProductClass(s)).Where(s => categories.Contains(s.CategoryName) && stocks.Contains(s.Barcode));
}
var group = item.Where(condition);
You can do it with IEnumerable interface in this way:
using System.Collections;
using System.Collections.Generic;
List<string> products = new List<string>() { "First", "Second", "Third", "Fourth" };
IEnumerable item;
var condition = false;
if (condition)
{
item = products.Select(x=>x);
}
else
{
item = products.Where(x => x.StartsWith("F"));
}
var group = item.Cast<string>().Where(/*...Here your conditions...*/)

Elegant way to check if a list contains an object where one property is the same, and replace only if the date of another property is later

I have a class as follows :
Object1{
int id;
DateTime time;
}
I have a list of Object1. I want to cycle through another list of Object1, search for an Object1 with the same ID and replace it in the first list if the time value is later than the time value in the list. If the item is not in the first list, then add it.
I'm sure there is an elegant way to do this, perhaps using linq? :
List<Object1> listOfNewestItems = new List<Object1>();
List<Object1> listToCycleThrough = MethodToReturnList();
foreach(Object1 object in listToCycleThrough){
if(listOfNewestItems.Contains(//object1 with same id as object))
{
//check date, replace if time property is > existing time property
} else {
listOfNewestItems.Add(object)
}
Obviously this is very messy (and that's without even doing the check of properties which is messier again...), is there a cleaner way to do this?
var finalList = list1.Concat(list2)
.GroupBy(x => x.id)
.Select(x => x.OrderByDescending(y=>y.time).First())
.ToList();
here is the full code to test
public class Object1
{
public int id;
public DateTime time;
}
List<Object1> list1 = new List<Object1>()
{
new Object1(){id=1,time=new DateTime(1991,1,1)},
new Object1(){id=2,time=new DateTime(1992,1,1)}
};
List<Object1> list2 = new List<Object1>()
{
new Object1(){id=1,time=new DateTime(2001,1,1)},
new Object1(){id=3,time=new DateTime(1993,1,1)}
};
and OUTPUT:
1 01.01.2001
2 01.01.1992
3 01.01.1993
This is how to check:
foreach(var object in listToCycleThrough)
{
var currentObject = listOfNewestItems
.SingleOrDefault(obj => obj.Id == object.Id);
if(currentObject != null)
{
if (currentObject.Time < object.Time)
currentObject.Time = object.Time
}
else
listOfNewestItems.Add(object)
}
But if you have large data, would be suggested to use Dictionary in newest list, time to look up will be O(1) instead of O(n)
You can use LINQ. Enumerable.Except to get the set difference(the newest), and join to find the newer objects.
var listOfNewestIDs = listOfNewestItems.Select(o => o.id);
var listToCycleIDs = listToCycleThrough.Select(o => o.id);
var newestIDs = listOfNewestIDs.Except(listToCycleIDs);
var newestObjects = from obj in listOfNewestItems
join objID in newestIDs on obj.id equals objID
select obj;
var updateObjects = from newObj in listOfNewestItems
join oldObj in listToCycleThrough on newObj.id equals oldObj.id
where newObj.time > oldObj.time
select new { oldObj, newObj };
foreach (var updObject in updateObjects)
updObject.oldObj.time = updObject.newObj.time;
listToCycleThrough.AddRange(newestObjects);
Note that you need to add using System.Linq;.
Here's a demo: http://ideone.com/2ASli
I'd create a Dictionary to lookup the index for an Id and use that
var newItems = new List<Object1> { ...
IList<Object1> itemsToUpdate = ...
var lookup = itemsToUpdate.
Select((i, o) => new { Key = o.id, Value = i }).
ToDictionary(i => i.Key, i => i.Value);
foreach (var newItem in newitems)
{
if (lookup.ContainsKey(newitem.ID))
{
var i = lookup[newItem.Id];
if (newItem.time > itemsToUpdate[i].time)
{
itemsToUpdate[i] = newItem;
}
}
else
{
itemsToUpdate.Add(newItem)
}
}
That way, you wouldn't need to reenumerate the list for each new item, you'd benefit for the hash lookup performance.
This should work however many times an Id is repeated in the list of new items.

Can I conditionally create an IEnumerable with LINQ?

I have to following code:
List<Obj> coll = new List<Obj>();
if (cond1) coll.Add(new Obj { /*...*/ });
if (cond2) coll.Add(new Obj { /*...*/ });
if (cond3) coll.Add(new Obj { /*...*/ });
Is there a way to use LINQ or collection initializers for that?
EDIT:
The reason I want to use a collection initializer here is because I have an object tree which I do completely initialize with initialiers and LINQ. This spot is the only one which doesn't follow this principle.
var myobj = new MyBigObj
{
Prop1 = from .. select ..,
Prop2 = from .. select ..,
...
Prop3 = new MySmallerObj
{
PropSmall1 = from .. select ..,
PropSmall2 = from .. select ..,
...
}
};
And now this simply doesn't fit in my scheme:
List<Obj> coll = new List<Obj>();
if (cond1) coll.Add(new Obj { /*...*/ });
if (cond2) coll.Add(new Obj { /*...*/ });
if (cond3) coll.Add(new Obj { /*...*/ });
myobj.Prop4 = coll;
Sure I could put this code in a separate function that returns IEnumerable and call that.. :)
EDIT2:
It looks like I have to code some extension method which I would call like:
new Obj[0]
.ConditionalConcat(cond1, x=>new Obj { /*...*/ })
.ConditionalConcat(cond2, x=>new Obj { /*...*/ })
.ConditionalConcat(cond3, x=>new Obj { /*...*/ })
One fairly horrible option:
var conditions = new[] { cond1, cond2, cond3 };
var values = new[] { new Obj {...}, // First value
new Obj {...}, // Second value
new Obj { ...} // Third value
};
var list = conditions.Zip(values, (condition, value) => new { condition, value })
.Where(pair => pair.condition)
.Select(pair => pair.value)
.ToList();
It's not exactly simpler than the original code though ;) (And also it unconditionally creates all the values - it's only conditionally including them in the collection.)
EDIT: An alternative which only constructs the values when it needs to:
var conditions = new[] { cond1, cond2, cond3 };
var valueProviders = new Func<Obj>[] {
() => new Obj {...}, // First value
() => new Obj {...}, // Second value
() => new Obj { ...} // Third value
};
var list = conditions.Zip(valueProviders,
(condition, provider) => new { condition, provider })
.Where(pair => pair.condition)
.Select(pair => pair.provider())
.ToList();
EDIT: Given your requested syntax, this is a fairly easy option:
new List<Obj>()
.ConditionalConcat(cond1, x=>new Obj { /*...*/ })
.ConditionalConcat(cond2, x=>new Obj { /*...*/ })
.ConditionalConcat(cond3, x=>new Obj { /*...*/ })
with an extension method:
public static List<T> ConditionalConcat<T>(this List<T> source,
bool condition,
Func<T> provider)
{
if (condition)
{
source.Add(provider);
}
return source;
}
If your conditions depend on a single status object (or something that can reduced to),
you can create a method using yield, like the following:
IEnumerable<Obj> GetElemets(MyStatus currentStatus)
{
if(currentStatus.Prop1 == "Foo")
yield return new Obj {...};
if(currentStatus.IsSomething())
yield return new Obj {...};
if(currentStatus.Items.Any())
yield return new Obj {...};
// etc...
yield break;
}
In this way, you will separate the IEnumerable<Obj> generation logic, from the consumer logic.
Old question, but here is another approach using ternery operators ? :, .Concat() and Enumerable.Empty<T>()
var range1 = Enumerable.Range(1,10);
var range2 = Enumerable.Range(100,10);
var range3 = Enumerable.Range(1000,10);
var flag1 = true;
var flag2 = false;
var flag3 = true;
var sumOfCollections = (flag1 ? range1 : Enumerable.Empty<int>())
.Concat(flag2 ? range2 : Enumerable.Empty<int>())
.Concat(flag3 ? range3 : Enumerable.Empty<int>());
Though an old question, but I have an option to solve it in a somewhat clear way and without extensions or any other methods.
Assuming that conditions and initial collection of objects to be created are of same size, I used indexed Where overload approach, so it is not adding objects conditionally, but rather filtering them, with a use of funcs/lambdas we get also laziness, if we want.
The actual creation of objects is not relevant, so I put just boxing of ints (you could replace it with real creation, i.e. getting them from another collection using index), and list manipulation is for getting ints back - but values collection already has 2 elements, so all this could be thrown away (maybe except select with func call in case of using laziness).
Here is the all the code for running sample test right in MSVS
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UnitTests
{
[TestClass]
public class Tests
{
[TestMethod]
public void Test()
{
var conds = new[] { true, false, true };
var values = conds.Select((c, i) => new Func<object>(() => i)).Where((f, i) => conds[i]);
var list = values.Select(f => f()).Cast<int>().ToList();
Assert.AreEqual(list.Count, 2);
}
}
}
UPD.
Here also lazy and non-lazy one-liners with "getting object"
var lazy1line = new[] { true, false, true }.Select((c, i) => new Func<object>(() => (DayOfWeek)i)).Where((f, i) => conds[i]).Select(f => f());
var simple1line = new[] { true, false, true }.Select((c, i) => (DayOfWeek)i).Where((f, i) => conds[i]);
Assert.AreEqual(lazy1line.Count(), simple1line.Count());

Categories