Is there a way to rewrite the GetTransformedCollection method below so that it uses a Linq statement and not an expression? I'm currently trying to get around the “A lambda expression with a statement body cannot be converted to an expression tree” error.
public class Obj1
{
public int Id { get; set; }
public string[] Names { get; set; }
public string[] Tags { get; set; }
}
public class EntCollections
{
private List<Obj1> _results;
[SetUp]
public void SetUp()
{
_results = new List<Obj1>
{
new Obj1 {Id = 1, Names = new[] {"n1"}, Tags = new[] {"abc", "def"}},
new Obj1 {Id = 2, Names = new[] {"n2", "n3"}, Tags = new[] {"ghi"}},
new Obj1 {Id = 3, Names = new[] {"n1", "n3"}, Tags = new[] {"def", "xyz"}}
};
}
private static Dictionary<string, List<string>>
GetTransformedCollection(IEnumerable<Obj1> results)
{
var list = new Dictionary<string, List<string>>();
foreach (var result in results)
{
foreach (var id in result.Names)
{
if (list.ContainsKey(id))
{
list[id].AddRange(result.Tags);
}
else
{
list.Add(id, result.Tags.ToList());
}
}
}
return list;
}
[Test]
public void Test()
{
var list = GetTransformedCollection(_results);
Assert.That(list["n1"], Is.EquivalentTo(new [] { "abc", "def", "def", "xyz" }));
Assert.That(list["n2"], Is.EquivalentTo(new [] { "ghi" }));
Assert.That(list["n3"], Is.EquivalentTo(new [] { "ghi", "def", "xyz" }));
}
P.s I'm not too worried about the result type being a Dictionary, that was just the simplist way to express it as a return type.
I would personally use an ILookup, which is a good bet whenever you have a Dictionary<T1, List<T2>>, and is built with ToLookup():
// Flatten the objects (lazily) to create a sequence of valid name/tag pairs
var pairs = from result in results
from name in result.Names
from tag in result.Tags
select new { name, tag };
// Build a lookup from name to all tags with that name
var lookup = pairs.ToLookup(pair => pair.name, pair => pair.tag);
Idea is to find all keys for resulting dictionary and then find corresponding values from original sequence of Obj1
var distinctNames = results.SelectMany(val => val.Names).Distinct();
return distinctNames
.ToDictionary(name => name,
name => results
.Where(res => res.Names.Contains(name))
.SelectMany(res => res.Tags)
.ToList());
Related
Is there a way to assert the equivalency of two lists of objects by properties located in a nested list? I know you can test equivalency with ShouldAllBeEquivalentTo() and Include() only certain properties, but I would like to call Include() on a property defined in a nested list:
class A
{
public B[] List { get; set; }
public string SomePropertyIDontCareAbout { get; set; }
}
class B
{
public string PropertyToInclude { get; set; }
public string SomePropertyIDontCareAbout { get; set; }
}
var list1 = new[]
{
new A
{
List = new[] {new B(), new B()}
},
};
var list2 = new[]
{
new A
{
List = new[] {new B(), new B()}
},
};
list1.ShouldAllBeEquivalentTo(list2, options => options
.Including(o => o.List.Select(l => l.PropertyToInclude))); // doesn't work
Currently there isn't an idiomatic way to achieve this, but the API is flexible enough to do it, albeit in a more clumpsy way.
There is an open issue about this problem, which also lists some solutions.
With the current API (version 5.7.0), your posted example can be asserted by only including the property List, and then excluding properties ending with "SomePropertyIDontCareAbout".
var list1 = new[]
{
new A
{
SomePropertyIDontCareAbout = "FOO",
List = new[]
{
new B()
{
PropertyToInclude = "BAR",
SomePropertyIDontCareAbout = "BAZ"
},
}
},
};
var list2 = new[]
{
new A
{
SomePropertyIDontCareAbout = "BOOM",
List = new[]
{
new B()
{
PropertyToInclude = "BAR",
SomePropertyIDontCareAbout = "BOOM"
},
}
},
};
// Assert
list1.Should().BeEquivalentTo(list2, opt => opt
.Including(e => e.List)
.Excluding(e => e.SelectedMemberPath.EndsWith(nameof(B.SomePropertyIDontCareAbout))));
Let's say, we have an object definition like this,
class MyObject {
int id;
string name;
}
and we have a list of MyObjects.
List<MyObject> objectList
Now, I need to partition this objectList into 2 sub-lists based on which objects have a non-null name field.
So, after the operation, I need to have 2 lists, objectsWithName where name field is non-null and objectsWithoutName where name field is null (this is the immediate criteria, but I'm looking more towards partitioning into 2 groups using a predicate).
What is the simplest way to achieve this? Can I do this in a single operation? Using LINQ is permitted.
Two LINQ statements would do:
var nameNotNull = objectList.Where(o => !string.IsNullOrEmpty(o.Name));
var nameNull = objectList.Where(o => string.IsNullOrEmpty(o.Name));
Of course, you could use GroupBy, or a more efficient foreach statement.
To show the foreach option:
List<MyObject> nameNotNull = new List<MyObject>();
List<MyObject> nameNull = new List<MyObject>();
foreach (MyObject o in objectList)
{
if (!string.IsNullOrEmpty(o.Name))
{
nameNotNull.Add(o);
}
else
{
nameNull.Add(o);
}
}
public class MyObject
{
public int Id { get; set; }
public string Name { get; set; }
}
public class MyObjectLists
{
private readonly List<MyObject> _objects;
public List<MyObject> NullNameObjects
{
get
{
return _objects.Where(x => x.Name == null).ToList();
}
}
public List<MyObject> NonNullNameObjects
{
get
{
return _objects.Where(x => x.Name != null).ToList();
}
}
public MyObjectLists(List<MyObject> objects)
{
_objects = objects ?? throw new ArgumentNullException(nameof(objects));
}
}
Using the code:
var list = new List<MyObject>
{
new MyObject
{
Id = 1,
Name = "John"
},
new MyObject
{
Id = 2
},
new MyObject
{
Id = 3,
Name = "Mary"
},
new MyObject
{
Id = 4
}
};
var objects = new MyObjectLists(list);
foreach (MyObject myObject in objects.NonNullNameObjects)
{
Console.WriteLine($"Object with Id {myObject.Id} has a non-null name");
}
foreach (MyObject myObject in objects.NullNameObjects)
{
Console.WriteLine($"Object with Id {myObject.Id} has a null name");
}
I think you're looking for something like this example:
class MyObject
{
int id;
string name;
}
var objectList = new List<MyObject>();
objectList.Add(new MyObject { name = "item 1" });
objectList.Add(new MyObject { name = string.Empty });
objectList.Add(new MyObject { name = "item 3" });
var objectsWithName = objectList.Where(x => !string.IsNullOrEmpty(x.name));
var objectsWithoutName = objectList.Except(objectsWithName);
I've created 2 lists that the first list contains the MyObject items with the name property is NOT null, otherwise to the second's.
Maybe this is a duplicate thread, but I am going to try, because there is a tiny difference.
I am trying to build a dynamic expression to filter a collection property.
The code:
public class TestEntity
{
public int ID { get; set; }
public string Name { get; set; }
public IEnumerable<string> Values { get; set; }
}
public class TestFilter
{
public TestFilter()
{
var itens = new List<TestEntity>();
itens.Add(new TestEntity { ID = 1, Name = "Test1", Values = new List<string> { "V1", "V2" } });
itens.Add(new TestEntity { ID = 2, Name = "Test2", Values = new List<string> { "V6", "V3" } });
itens.Add(new TestEntity { ID = 3, Name = "Test3", Values = new List<string> { "V4", "V5" } });
itens.Add(new TestEntity { ID = 4, Name = "Test4", Values = new List<string> { "V2", "V3" } });
itens = itens.Where(e => e.Values.Any(c => c.Equals("V2"))).ToList();
**//Result IDs: 1, 4**
}
}
The filter above will give me IDs 1 and 4 as result.
I want to filter entities where exists a certain value in the collection "Values".
So far, I have tried this thread, but didnt realize how it can be done.
Any help would be apreciated.
So you are seeking for a method which given a collection property name and value will produce Where predicate like e => e.Collection.Any(c => c == value).
You can use the following extension method (hope the code is self explanatory):
public static class QueryableExtensions
{
public static IQueryable<T> WhereAnyEquals<T>(this IQueryable<T> source, string collectionName, object value)
{
var e = Expression.Parameter(typeof(T), "e");
var collection = Expression.PropertyOrField(e, collectionName);
var itemType = (collection.Type.IsIEnumerableT() ? collection.Type :
collection.Type.GetInterfaces().Single(IsIEnumerableT))
.GetGenericArguments()[0];
var c = Expression.Parameter(itemType, "c");
var itemPredicate = Expression.Lambda(
Expression.Equal(c, Expression.Constant(value)),
c);
var callAny = Expression.Call(
typeof(Enumerable), "Any", new Type[] { itemType },
collection, itemPredicate);
var predicate = Expression.Lambda<Func<T, bool>>(callAny, e);
return source.Where(predicate);
}
private static bool IsIEnumerableT(this Type type)
{
return type.IsInterface && type.IsConstructedGenericType &&
type.GetGenericTypeDefinition() == typeof(IEnumerable<>);
}
}
like this:
itens = itens.AsQueryable().WhereAnyEquals("Values", "V2").ToList();
If you step through the code, the variable predicate contains the expression you are asking for.
I have a class and an array of property names defined as follows:
public class Dog {
public string Name { get; set; }
public string Breed { get; set; }
public int Age { get; set; }
}
var desiredProperties = new [] {"Name", "Breed"};
I also have a method that returns a list of dog objects:
List<Dog> dogs = GetAllDogs();
Is there an way I can return a subset of dogs that only contain the properties defined within the desiredProperties array? Eventually, this resulting list will be serialized to JSON.
I have been struggling with this problem for some time now, considering that the user will be allowed to specify any combination of properties (assuming they are all valid) as the output within the array. Some more examples:
var desiredProperties = new [] {"Name", "Age"};
// Sample output, when serialized to JSON:
// [
// { Name: "Max", Age: 5 },
// { Name: "Spot", Age: 2 }
// ]
var desiredProperties = new [] {"Breed", "Age"};
// [
// { Breed: "Scottish Terrier", Age: 5 },
// { Breed: "Cairn Terrier", Age: 2 }
// ]
you can write a function to do that. Use the extension method below.
public static class Extensions
{
public static object GetPropertyValue(this object obj, string propertyName)
{
return obj.GetType().GetProperty(propertyName).GetValue(obj);
}
public static List<Dictionary<string, object>> FilterProperties<T>(this IEnumerable<T> input, IEnumerable<string> properties)
{
return input.Select(x =>
{
var d = new Dictionary<string, object>();
foreach (var p in properties)
{
d[p] = x.GetPropertyValue(p);
}
return d;
}).ToList();
}
}
Test it like
var dogs = GetAllDogs();
var f1 = dogs.FilterProperties(new[]
{
"Name", "Age"
});
var f2 = dogs.FilterProperties(new[]
{
"Breed", "Age"
});
Console.WriteLine(JsonConvert.SerializeObject(f1));
Console.WriteLine(JsonConvert.SerializeObject(f2));
and result is
[{"Name":"Spot","Age":2},{"Name":"Max","Age":5}]
[{"Breed":"Cairn Terrier","Age":2},{"Breed":"Scottish Terrier","Age":5}]
I don't have a clue if this is the most efficient way to do it, but it's a way of doing it:
var list = new List<Dog>();
list.Add(new Dog {Name = "Max", Breed = "Bull Terrier", Age = 5});
list.Add(new Dog {Name = "Woofie", Breed = "Collie", Age = 3});
var desiredProperties = new[] {"Name", "Breed"};
var exportDogs = new List<Dictionary<string, object>>();
foreach(var dog in list)
{
var exportDog = new Dictionary<string, object>();
foreach(var property in desiredProperties)
{
exportDog[property] = dog.GetType().GetProperty(property).GetValue(dog, null);
}
exportDogs.Add(exportDog);
}
var output = JsonConvert.SerializeObject(exportDogs);
The output will look like this:
[{"Name":"Max","Breed":"Bull Terrier"},{"Name":"Woofie","Breed":"Collie"}]
If, however, you don't need to dynamically access properties, it's a lot better to do something like this:
var output = list.Select(dog => new {dog.Name, dog.Breed});
Then just serialize the output.
something like this...not tested...
var desiredProperties = new [] {"Name", "Breed"};
var lst = (from asm in AppDomain.CurrentDomain.GetAssemblies()
from asmTyp in asm.GetTypes()
where typeof(dog).IsAssignableFrom(asmTyp) && desiredProperties.All(p=> PropertyExists(asmTyp, p))
select asmTyp).ToArray();
private bool PropertyExists(Type dogType, string name)
{
bool ret=true;
try{ dogType.GetProperty(name);}
catch{ret=false};
return ret;
}
I struggle with generating all possible combinations for a List of Attributes with their possible values. What I would like to implement is a Method like this:
public List<Variant> generateAllPossibleVariants(List<Attribute> attributes)
The Attribute Class looks the following:
public class Attribute {
public String Name { get; set; }
public ICollection<String> PossibleValues { get; protected set; }
}
So imagine you have a list of 2 Attributes (while the count is dynamic) with their possible values:
attributeColor with possible Values of ( "red", "blue" )
attributeSize with possible values of ("XL", "L" )
Now my method should return a List of Variant while the Variant Class looks the following:
public class Variant
{
public IDictionary<Attribute, string> AttributeValues { get; private set; }
}
Now my method should return a List of all combinations like the following:
List<Variant> :
Variant.AttributeValues { attributeColor => "red", attributeSize => "XL" }
Variant.AttributeValues { attributeColor => "red", attributeSize => "L" }
Variant.AttributeValues { attributeColor => "blue", attributeSize => "XL" }
Variant.AttributeValues { attributeColor => "blue", attributeSize => "L" }
This is not an optimized code, but you can get the idea and clean it up yourself:
public void Main()
{
var attr1 = new MyAttribute { Name = "Colors", PossibleValues = new List<string> { "red", "blue" } };
var attr2 = new MyAttribute { Name = "Sizes", PossibleValues = new List<string> { "XL", "L" } };
var attr3 = new MyAttribute { Name = "Shapes", PossibleValues = new List<string> { "qube", "circle" } };
var attrList = new List<MyAttribute> { attr1, attr2, attr3 };
var result = attrList.Skip(1).Aggregate<MyAttribute, List<Variant>>(
new List<Variant>(attrList[0].PossibleValues.Select(s => new Variant { AttributeValues = new Dictionary<MyAttribute, string> { {attrList[0], s} } })),
(acc, atr) =>
{
var aggregateResult = new List<Variant>();
foreach (var createdVariant in acc)
{
foreach (var possibleValue in atr.PossibleValues)
{
var newVariant = new Variant { AttributeValues = new Dictionary<MyAttribute, string>(createdVariant.AttributeValues) };
newVariant.AttributeValues[atr] = possibleValue;
aggregateResult.Add(newVariant);
}
}
return aggregateResult;
});
}
public class MyAttribute
{
public string Name { get; set; }
public ICollection<string> PossibleValues { get; set; }
}
public class Variant
{
public IDictionary<MyAttribute, string> AttributeValues { get; set; }
}
You are looking for cartesian product (with dynamic dimension).
One simple way to achieve it is using recursion on the dimension, and each time invoke cartesian product on the result of the recursion, and one of the dimensions.
Pseudo code:
genAllPossibilities(list attributes) //each element in attributes is a list
if length(attributes) == 1:
return attributes[0] //the only element, which is a list of one attribute
else:
curr = head(attributes) // first element in the list
reminder = tails(attributes) // a list of all elements except the first
return cartesianProduct(genAllPossibilities(reminder), curr)
cartesianProduct(list1, list2):
l = new list
for each x1 in list1:
for each x2 in list2:
l.append(new MyObject(x1,x2))
return l