I have following two list:
var firstList = new List<ProgramInfo> ()
{
new ProgramInfo {Name = "A", ProgramId = 1, Description = "some text1"},
new ProgramInfo {Name = "C", ProgramId = 2, Description = "some text2"},
new ProgramInfo {Name = "D", ProgramId = 3, Description = "some text3"},
new ProgramInfo {Name = "E", ProgramId = 4, Description = "some text4"}
};
var secondList = new List<ProgramInfo> ()
{
new ProgramInfo {Name = "C", ProgramId = 2, Description = "some text1"},
new ProgramInfo {Name = "D", ProgramId = 3, Description = "some text2"},
};
this two list gets generated at runtime and I have to select the common ProgramInfo depending on the program id from both of this list
for example, in case of above example the output should be
var thirdList = new List<ProgramInfo>()
{
new ProgramInfo {Name = "C", ProgramId = 2, Description = "some text1"},
new ProgramInfo {Name = "D", ProgramId = 3, Description = "some text2"},
};
public class ProgramInfo
{
public string Name { get; set; }
public int ProgramId { get; set; }
public string Description { get; set; }
}
Can someone suggest me how can I do this using lambda expression?
Use Linq .Intersect. For that to work your class needs to override the Equals and GetHashCode
var thirdList = firstList.Intersect(secondList);
You can also specify an IEqualityComparer instead of overriding the functions:
public class Comparer : IEqualityComparer<ProgramInfo>
{
public bool Equals(ProgramInfo x, ProgramInfo y)
{
return x.Name == y.Name &&
x.ProgramId == y.ProgramId &&
x.Description == y.Description;
}
public int GetHashCode(ProgramInfo obj)
{
return obj.Name.GetHashCode() ^
obj.ProgramId.GetHashCode() ^
obj.Description.GetHashCode();
}
}
var thirdList = firstList.Intersect(secondList, new Comparer());
Related
I haw this model:
public class Obj1
{
public long ID { get; set; }
public long Name { get; set; }
public List<int> NumberList { get; set; }
}
The values in Obj1 List:
List<Obj1> lst1 = new List<Obj1>();
lst1.Add(new Obj1()
{
ID = 1,
Name = "t1",
NumberList = new List<int>{1,3,4}
});
lst1.Add(new Obj1()
{
ID = 2,
Name = "t2",
NumberList = new List<int>{1,4,5}
});
lst1.Add(new Obj1()
{
ID = 3,
Name = "t3",
NumberList = new List<int>{4,5,6}
});
lst1.Add(new Obj1()
{
ID = 4,
Name = "t4",
NumberList = new List<int>{5,7,8}
});
I want to select list1 provided all list2 is in it. Also list 2 is equal to:
List<int> lst2 = new List<int>(){4,5};
I use this code, But it doesn't work properly:
var FinalList = lst1.Where(item => item.NumberList.Any(item2 => lst2.Contains(item2)).ToList();
The correct output should be this:
{
ID = 2,
Name = "t2",
NumberList = new List<int>{1,4,5}
},
{
ID = 3,
Name = "t3",
NumberList = new List<int>{4,5,6}
}
If I understand correctly, you want all Obj items in lst1 if the NumberList property is a superset of lst2. You can query this like so:
var finalList = lst1.Where(l => !lst2.Except(l.NumberList).Any()).ToList();
Full example on Ideone.
In this contrived example, which closely resembles my real-world problem, I have a data set coming from an external source. Each record from the external source takes the following form:
[Classification] NVARCHAR(32),
[Rank] INT,
[Data] NVARCHAR(1024)
I am looking to build an object where the Rank and Data are patched into a single instance of a response object that contains list properties for the three hard-coded Classification values, ordered by Rank.
I have something that works, but I can't help but think that it could be done better. This is what I have:
public static void Main()
{
IEnumerable<GroupingTestRecord> records = new List<GroupingTestRecord>
{
new GroupingTestRecord { Classification = "A", Rank = 1, Data = "A1" },
new GroupingTestRecord { Classification = "A", Rank = 2, Data = "A2" },
new GroupingTestRecord { Classification = "A", Rank = 3, Data = "A3" },
new GroupingTestRecord { Classification = "B", Rank = 1, Data = "B1" },
new GroupingTestRecord { Classification = "B", Rank = 2, Data = "B2" },
new GroupingTestRecord { Classification = "B", Rank = 3, Data = "B3" },
new GroupingTestRecord { Classification = "C", Rank = 1, Data = "C1" },
new GroupingTestRecord { Classification = "C", Rank = 2, Data = "C2" },
new GroupingTestRecord { Classification = "C", Rank = 3, Data = "C3" },
};
GroupTestResult r = new GroupTestResult
{
A = records.Where(i => i.Classification == "A").Select(j => new GroupTestResultItem { Rank = j.Rank, Data = j.Data, }).OrderBy(k => k.Rank),
B = records.Where(i => i.Classification == "B").Select(j => new GroupTestResultItem { Rank = j.Rank, Data = j.Data, }).OrderBy(k => k.Rank),
C = records.Where(i => i.Classification == "C").Select(j => new GroupTestResultItem { Rank = j.Rank, Data = j.Data, }).OrderBy(k => k.Rank),
};
The source record DTO:
public class GroupingTestRecord
{
public string Classification { get; set; }
public int? Rank { get; set; }
public string Data { get; set; }
}
The destination single class:
public class GroupTestResult
{
public IEnumerable<GroupTestResultItem> A { get; set; }
public IEnumerable<GroupTestResultItem> B { get; set; }
public IEnumerable<GroupTestResultItem> C { get; set; }
}
The distination child class:
public class GroupTestResultItem
{
public int? Rank { get; set; }
public string Data { get; set; }
}
Ouput
{
"A":[
{
"Rank":1,
"Data":"A1"
},
{
"Rank":2,
"Data":"A2"
},
{
"Rank":3,
"Data":"A3"
}
],
"B":[
{
"Rank":1,
"Data":"B1"
},
{
"Rank":2,
"Data":"B2"
},
{
"Rank":3,
"Data":"B3"
}
],
"C":[
{
"Rank":1,
"Data":"C1"
},
{
"Rank":2,
"Data":"C2"
},
{
"Rank":3,
"Data":"C3"
}
]
}
Fiddle
Is there a better way to achieve my goal here?
The same JSON output was achieved using GroupBy first on the Classification and applying ToDictionary on the resulting IGrouping<string, GroupingTestRecord>.Key
var r = records
.GroupBy(_ => _.Classification)
.ToDictionary(
k => k.Key,
v => v.Select(j => new GroupTestResultItem { Rank = j.Rank, Data = j.Data, }).OrderBy(k => k.Rank).ToArray()
);
var json = JsonConvert.SerializeObject(r);
Console.WriteLine(json);
which should easily deserialize to the destination single class (for example on a client)
var result = JsonConvert.DeserializeObject<GroupTestResult>(json);
is it possible to get the top level result into a GroupTestResult object?
Build the result from the dictionary
var result = new GroupTestResult {
A = r.ContainsKey("A") ? r["A"] : Enumerable.Empty<GroupTestResultItem>();,
B = r.ContainsKey("B") ? r["B"] : Enumerable.Empty<GroupTestResultItem>();,
C = r.ContainsKey("C") ? r["C"] : Enumerable.Empty<GroupTestResultItem>();,
};
Or this
var result = records.GroupBy(x => x.Classification)
.ToDictionary(x => x.Key, x => x.Select(y => new {y.Rank, y.Data})
.OrderBy(y => y.Rank));
Console.WriteLine(JsonConvert.SerializeObject(result));
Full Demo Here
I have an Array of colors viz.
var colorPallete = new string[]{color1, color2, color3, color4, color5};
I also have a list of objects which contains an ID.
eg. var previousList<MyModel> = new List<MyModel>();
MyModel.cs
public class MyModel()
{
public int ID {get; set;}
public string Class{get; set;}
public string Name {get; set;}
public string Color {get; set;}
}
I want to assign the objects with same ID with a certain color. And then add the assigned color as a new value to the list.
for eg:
Previous list :-
ID :1
Name: abc
Class: Senior
ID :2
Name: xyz
Class: Medium
ID :3
Name: pqr
Class: junior
ID :1
Name: mno
Class: junior
New List :-
ID :1
Name: abc
Class: Senior
Color :color1
ID :2
Name: xyz
Class: Medium
Color :color2
ID :3
Name: pqr
Class: junior
Color :color3
ID :1
Name: mno
Class: junior
Color :color1
This works for me:
var colorPallete = new string[]
{
"color1", "color2", "color3", "color4", "color5",
};
var previousList = new []
{
new { ID = 1, Name = "abc", Class = "Senior", },
new { ID = 2, Name = "xyz", Class = "Medium", },
new { ID = 3, Name = "pqr", Class = "junior", },
new { ID = 1, Name = "mno", Class = "junior", },
};
var newList =
previousList
.Select(x => new
{
x.ID,
x.Name,
x.Class,
Color = colorPallete.ElementAtOrDefault(x.ID - 1),
})
.ToList();
I get this result:
With the question update providing the class MyModel the code can then be written like so:
var colorPallete = new string[]
{
"color1", "color2", "color3", "color4", "color5",
};
var previousList = new List<MyModel>()
{
new MyModel() { ID = 1, Name = "abc", Class = "Senior", },
new MyModel() { ID = 2, Name = "xyz", Class = "Medium", },
new MyModel() { ID = 3, Name = "pqr", Class = "junior", },
new MyModel() { ID = 1, Name = "mno", Class = "junior", },
};
var newList =
previousList
.Select(x => new MyModel()
{
ID = x.ID,
Name = x.Name,
Class = x.Class,
Color = colorPallete.ElementAtOrDefault(x.ID - 1),
})
.ToList();
Which gives:
Now, this approach produces a new list keeping the old list and the old objects intact. Generally this is what you should try to do. It's best to mutate objects only when you know that's what they're designed to do.
So it becomes possible to do an in-place update of the original list like so:
previousList.ForEach(x => x.Color = colorPallete.ElementAtOrDefault(x.ID - 1));
This results in modifying the previousList objects without creating a newList.
If you are using List<T> (not IEnumerable<T>) and you don't want to create a new list, but need to update values in the existing list instead, you can do it with the single query. There are three ways to process your scenario (A, B, C):
var colorPallete = new string[]
{
"Red", "Green", "Blue"
};
var list = new List<MyModel>()
{
new MyModel() { ID = 1, Name = "model1", Class = "A", },
new MyModel() { ID = 1, Name = "model11", Class = "AA", },
new MyModel() { ID = 2, Name = "model2", Class = "B", },
new MyModel() { ID = 3, Name = "model3", Class = "C", },
new MyModel() { ID = 4, Name = "model4", Class = "D", },
new MyModel() { ID = 5, Name = "model5", Class = "E", },
};
//A. This code assigns null for unknown IDs
//I.e. if (ID > 0 && ID < colorPallete.Length) then color will be picked from colorPallete[],
//else it will be null
list.ForEach(x => x.Color = colorPallete.ElementAtOrDefault(x.ID - 1));
//B. This code apply some default color for unknown IDs
//I.e. if (ID > 0 && ID < colorPallete.Length) then color will be picked from colorPallete,
//else it will be "DefaultColor"
list.ForEach(x => x.Color = colorPallete.ElementAtOrDefault(x.ID - 1) ?? "DefaultColor");
//C. This code can assign the same color to models with different IDs,
//but models with identical IDs always will have identical color
list.ForEach(x => x.Color = colorPallete.ElementAtOrDefault((x.ID - 1) % colorPallete.Length));
I would create a class for the objects with a color property like this:
public class MyClass
{
public int ID { get; set; }
public string Name { get; set; }
public string Class { get; set; }
public string Color { get; set; } // Nullable
}
And for the colors I would create another class with an ID to compare with the ID of MyClass:
public class MyColor
{
public int ID { get; set; }
public string Color { get; set; }
}
For each color in colorPalette you would assign an ID that matches the ID of the list of MyClass.
So at first the color from MyClass would be null. And then you could loop over the list of MyClass:
foreach (MyClass myClass in myClassList)
{
myClass.Color = colorPalette.FirstOrDefault(col => col.ID = myClass.ID);
}
Or without an ID in Color class (comparing the names of the variables which is not a beautiful solution):
foreach (MyClass myClass in myClassList)
{
myClass.Color = colorPalette.FirstOrDefault(col => int.Parse(nameof(col.Color).Replace("color", "")) == myClass.ID);
}
i have two generic lists with a few properties to compare but i want that the key identifiers are dynamic by a List<string>.
So lets say we have the class:
class A
{
string Name { get; set; }
string Color1 { get; set; }
string Color2 { get; set; }
string Length { get; set; }
}
The user now can select from an user interface which properties of two lists of those objects need to overlap so that a correct pair is selected. This is stored in a List<string>. As example, if the list string contains "Name" and "Color1" there will be only objects returned where "Name" and "Color1" are overlapping.
I was trying to write a function, but unfortunately i'm not sure which collection i should cast the generic lists to and how do i apply the names of the properties on those? If the name of the "identificators" were always the same, it wouldn't be a problem with Linq/Lambda ;)
Thanks in advance
You need to use reflection for this. This works:
public class A
{
public string Name { get; set; }
public string Color1 { get; set; }
public string Color2 { get; set; }
public string Length { get; set; }
public static IEnumerable<A> Intersecting(IEnumerable<A> input, List<string> propertyNames)
{
if(input == null)
throw new ArgumentNullException("input must not be null ", "input");
if (!input.Any() || propertyNames.Count <= 1)
return input;
var properties = typeof(A).GetProperties();
var validNames = properties.Select(p => p.Name);
if (propertyNames.Except(validNames, StringComparer.InvariantCultureIgnoreCase).Any())
throw new ArgumentException("All properties must be one of these: " + string.Join(",", validNames), "propertyNames");
var props = from prop in properties
join name in validNames.Intersect(propertyNames, StringComparer.InvariantCultureIgnoreCase)
on prop.Name equals name
select prop;
var allIntersecting = input
.Select(a => new {
Object = a,
FirstVal = props.First().GetValue(a, null),
Rest = props.Skip(1).Select(p => p.GetValue(a, null)),
})
.Select(x => new {
x.Object, x.FirstVal, x.Rest,
UniqueValues = new HashSet<object>{ x.FirstVal }
})
.Where(x => x.Rest.All(v => !x.UniqueValues.Add(v)))
.Select(x => x.Object);
return allIntersecting;
}
}
Sample data:
var aList = new List<A> {
new A { Color1 = "Red", Length = "2", Name = "Red" }, new A { Color1 = "Blue", Length = "2", Name = "Blue" },
new A { Color1 = "Red", Length = "2", Name = "A3" }, new A { Color1 = "Blue", Length = "2", Name = "A3" },
new A { Color1 = "Red", Length = "3", Name = "Red" }, new A { Color1 = "Blue", Length = "2", Name = "A6" },
};
var intersecting = A.Intersecting(aList, new List<string> { "Color1", "Name" }).ToList();
I've managed to implement $inlinecount with WebApi.OData (v 4.0.0) using the ODataQueryOptions<T> and PageResult<T> classes like this:
POCO
public class Poco
{
public int id { get; set; }
public string name { get; set; }
public string type { get; set; }
}
Controller
[ActionName("Default")]
public PageResult<Poco> Get(ODataQueryOptions<Poco> queryOptions)
{
var data = new Poco[] {
new Poco() { id = 1, name = "one", type = "a" },
new Poco() { id = 2, name = "two", type = "b" },
new Poco() { id = 3, name = "three", type = "c" },
new Poco() { id = 4, name = "four", type = "d" },
new Poco() { id = 5, name = "five", type = "e" },
new Poco() { id = 6, name = "six", type = "f" },
new Poco() { id = 7, name = "seven", type = "g" },
new Poco() { id = 8, name = "eight", type = "h" },
new Poco() { id = 9, name = "nine", type = "i" }
};
var t = new ODataValidationSettings() { MaxTop = 2 };
queryOptions.Validate(t);
var s = new ODataQuerySettings() { PageSize = 1 };
IQueryable results = queryOptions.ApplyTo(data.AsQueryable(), s);
var next = Request.GetNextPageLink();
var count = Request.GetInlineCount();
return new System.Web.Http.OData.PageResult<Poco>(
results as IEnumerable<Poco>, next, count);
}
I'm getting error 406 when I switch from JSON to old school XmlSerializer. Does anyone know if this should work?
var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
xml.UseXmlSerializer = true;
GlobalConfiguration.Configuration.Formatters.Remove(
GlobalConfiguration.Configuration.Formatters.JsonFormatter);
PageResult can't be serialized by XmlSerializer because it doesn't have a public, parameterless constructor. But there's nothing stopping you from defining your own similar type that does have a public, parameterless constructor. It should be pretty simple to do. I'd recommend taking a look at the source code for PageResult and adopting a similar approach.