C# Append lists in a list - c#

i have a loop that fetch some countries in my DB and according to a condition i need to put some value in that array and then at the end have a final list with all values. Note that i will need 3 checks ( level 1, 2 and 3) inside the loop:
var countries= db.Countries
.Where(x => x.name.Contains(searchText))
.OrderByDescending(o => o.id);
foreach (var country in countries)
{
if(country.Cities.Count() == 1)
{
customList.Add(new CustomClass
{
countCities = 1,
name = country.name,
level = "low"
});
}
else if (country.Cities.Count() == 2)
{
customList.Add(new CustomClass
{
countCities = 2,
name = country.name
level = "medium"
});
}
else if (country.Cities.Count() == 3)
{
customList.Add(new CustomClass
{
countCities = 3,
name = country.name
level = "high"
});
}
}
at the end i want to have something like this :
[ { name="Canada", countCities = 1, level ="low"}, { name = "Australia", countCities = 2, level ="medium"}, { name = "China", countCities = 3, level ="high"} ]
In PHP i know how to do it, but with c# im stuck !

customArray.AddRange( db.Countries
.Where(x => x.name.Contains(searchText))
.OrderByDescending(o => o.id)
.Select(c => new CustomClass { name = c.name, countCities = c.Cities.Count() })
);
Where CustomClass has a simple getter that returns a 'level' label does what you want but with less code
public string level {
get {
return countCities <= 1 ? "low" : (countCities == 2 ? "medium" : "high");
}
}

Use generic lists, those can be easily expanded:
List<string> stringarray = new List<string>();
stringarray.Add("New entry");
Combine them like this:
List<List<string>> lls = new List<List<string>>();
lls.Add(stringarray);
// Don't forget to re-initialize the startingarray if you want to reuse it.
stringarray = new List<string>();
If you want, you can even create a custom class 'CustomClass' and list that:
List<CustomClass> lcc = new List<CustomClass>();

I would eliminate the "SearchText"
customArray.AddRange(from con in db.Countries
join cit in db.Cities on con.id equals cit.id
orderby con.id
select new CustomClass { name= con.name, countCities= con.Cities.Count() }
);

I found it !
for (var index = 0; index <= countries.Count()-1; index++)
{ if ()...
else if()...
}
is the trick !
Dont vote down if you dont have the good answer and try to have your own question. Thanks

Related

Using LINQ, how would you filter out all but one item of a particular criteria from a list?

I realize my title probably isn't very clear so here's an example:
I have a list of objects with two properties, A and B.
public class Item
{
public int A { get; set; }
public int B { get; set; }
}
var list = new List<Item>
{
new Item() { A = 0, B = 0 },
new Item() { A = 0, B = 1 },
new Item() { A = 1, B = 0 },
new Item() { A = 2, B = 0 },
new Item() { A = 2, B = 1 },
new Item() { A = 2, B = 2 },
new Item() { A = 3, B = 0 },
new Item() { A = 3, B = 1 },
}
Using LINQ, what's the most elegant way to collapse all the A = 2 items into the first A = 2 item and return along with all the other items? This would be the expected result.
var list = new List<Item>
{
new Item() { A = 0, B = 0 },
new Item() { A = 0, B = 1 },
new Item() { A = 1, B = 0 },
new Item() { A = 2, B = 0 },
new Item() { A = 3, B = 0 },
new Item() { A = 3, B = 1 },
}
I'm not a LINQ expert and already have a "manual" solution but I really like the expressiveness of LINQ and was curious to see if it could be done better.
How about:
var collapsed = list.GroupBy(i => i.A)
.SelectMany(g => g.Key == 2 ? g.Take(1) : g);
The idea is to first group them by A and then select those again (flattening it with .SelectMany) but in the case of the Key being the one we want to collapse, we just take the first entry with Take(1).
One way you can accomplish this is with GroupBy. Group the items by A, and use a SelectMany to project each group into a flat list again. In the SelectMany, check if A is 2 and if so Take(1), otherwise return all results for that group. We're using Take instead of First because the result has to be IEnumerable.
var grouped = list.GroupBy(g => g.A);
var collapsed = grouped.SelectMany(g =>
{
if (g.Key == 2)
{
return g.Take(1);
}
return g;
});
One possible solution (if you insist on LINQ):
int a = 2;
var output = list.GroupBy(o => o.A == a ? a.ToString() : Guid.NewGuid().ToString())
.Select(g => g.First())
.ToList();
Group all items with A=2 into group with key equal to 2, but all other items will have unique group key (new guid), so you will have many groups having one item. Then from each group we take first item.
Yet another way:
var newlist = list.Where (l => l.A != 2 ).ToList();
newlist.Add( list.First (l => l.A == 2) );
An alternative to other answers based on GroupBy can be Aggregate:
// Aggregate lets iterate a sequence and accumulate a result (the first arg)
var list2 = list.Aggregate(new List<Item>(), (result, next) => {
// This will add the item in the source sequence either
// if A != 2 or, if it's A == 2, it will check that there's no A == 2
// already in the resulting sequence!
if(next.A != 2 || !result.Any(item => item.A == 2)) result.Add(next);
return result;
});
What about this:
list.RemoveAll(l => l.A == 2 && l != list.FirstOrDefault(i => i.A == 2));
if you whould like more efficient way it would be:
var first = list.FirstOrDefault(i => i.A == 2);
list.RemoveAll(l => l.A == 2 && l != first);

How to return Distinct Row using LINQ

I have two rows which have all the data same except one column.
I want to show only one row on the UI but one row which has different data should be shown as comma seperated values.
Sample Data
PricingID Name Age Group
1 abc 56 P1
1 abc 56 P2
Output should be :
PricingID Name Age Group
1 abc 56 P1,P2
I am using this approach but it is not working , it gives me two rows only but data i am able to concatenate with comma.
List<PricingDetailExtended> pricingDetailExtendeds = _storedProcedures.GetPricingAssignment(pricingScenarioName, regionCode, productCode, stateCode, UserId, PricingId).ToList();
var pricngtemp = pricingDetailExtendeds.Select(e => new
{
PricingID = e.PricingID,
OpportunityID = e.OpportunityID,
ProductName = e.ProductName,
ProductCD = e.ProductCD
});
pricingDetailExtendeds.ForEach(e=>
{
e.ProductCD = string.Join(",",string.Join(",", (pricngtemp.ToList().Where(p => p.PricingID == e.PricingID).Select(k => k.ProductCD).ToArray())).Split(',').Distinct().ToArray());
e.OpportunityID =string.Join(",", string.Join(",", (pricngtemp.ToList().Where(p => p.PricingID == e.PricingID).Select(k => k.OpportunityID).ToArray())).Split(',').Distinct().ToArray());
e.ProductName =string.Join(",", string.Join(",", (pricngtemp.ToList().Where(p => p.PricingID == e.PricingID).Select(k => k.ProductName).ToArray())).Split(',').Distinct().ToArray());
}
);
// pricingDetailExtendeds = GetUniquePricingList(pricingDetailExtendeds);
return pricingDetailExtendeds.Distinct().AsEnumerable();
Any body can suggest me better approach and how to fix this issue ?
Any help is appreciated.
You want to use the GroupBy linq function.
I then use the String.Join function to make the groups comma seperated.
So something like this:
var pricingDetailExtendeds = new[]
{
new
{
PricingID = 1,
Name = "abc",
Age = 56,
Group = "P1"
},
new
{
PricingID = 1,
Name = "abc",
Age = 56,
Group = "P2"
}
};
var pricngtemp =
pricingDetailExtendeds.GroupBy(pde => new {pde.PricingID, pde.Name, pde.Age})
.Select(g => new {g.Key, TheGroups = String.Join(",", g.Select(s => s.Group))}).ToList();
You can easily extrapolate this to the other fields.
To return the PricingDetailExtended, the just create it in the select. So something like this
.Select(g => new PricingDetailExtended {
PricingID = g.Key.PricingId,
TheGroups = String.Join(",", g.Select(s => s.Group))
}).ToList();
You won't have the field TheGroups though, so just replace that field with the proper one.
An example of what I was describing in my comment would be something along the lines of the following. I would expect this to be moved into a helper function.
List<PriceDetail> list = new List<PriceDetail>
{
new PriceDetail {Id = 1, Age = 56, Name = "abc", group = "P1"},
new PriceDetail {Id = 1, Age = 56, Name = "abc", group = "P2"},
new PriceDetail {Id = 2, Age = 56, Name = "abc", group = "P1"}
};
Dictionary<PriceDetailKey, StringBuilder> group = new Dictionary<PriceDetailKey, StringBuilder>();
for (int i = 0; i < list.Count; ++i)
{
var key = new PriceDetailKey { Id = list[i].Id, Age = list[i].Age, Name = list[i].Name };
if (group.ContainsKey(key))
{
group[key].Append(",");
group[key].Append(list[i].group);
}
else
{
group[key] = new StringBuilder();
group[key].Append(list[i].group);
}
}
List<PriceDetail> retList = new List<PriceDetail>();
foreach (KeyValuePair<PriceDetailKey, StringBuilder> kvp in group)
{
retList.Add(new PriceDetail{Age = kvp.Key.Age, Id = kvp.Key.Id, Name = kvp.Key.Name, group = kvp.Value.ToString()});
}
you could even convert the final loop into a LINQ expression like:
group.Select(kvp => new PriceDetail {Age = kvp.Key.Age, Id = kvp.Key.Id, Name = kvp.Key.Name, group = kvp.Value.ToString()});
Its worth noting you could do something similar without the overhead of constructing new objects if, for example, you wrote a custom equality comparer and used a list instead of dictionary. The upside of that is that when you were finished, it would be your return value without having to do another iteration.
There are several different ways to get the results. You could even do the grouping in SQL.

Conditional Where clause in LINQ

suppose i am showing data in grid and i have many textboxes for filter the data.
textbox for employee id. if employee id textbox is empty then no where clause will be added but if it is not empty then where clause will be added for that. the same way we can filter data if salary textbox has value or employee name textbox has value.
i try to compose a conditional LINQ query but got error. here is mine
var sName="";
var r = from t in TblFamilies
where 1 == 1
if(sName!="")
{
&& t.Name="Keith";
};
select new
{
t.ID,
t.ParentID,
t.Name,
t.CurDate
};
r.Dump();
Try this:-
First select the data:-
var r = from t in TblFamilie
select new
{
t.ID,
t.ParentID,
t.Name,
t.CurDate
};
Then you can filter based on condition:-
if (sName!="")
r = r.Where(x => x.Name == sName);
If you want to mix And operator and Or operator together, check PredicateBuilder out here: http://www.albahari.com/nutshell/predicatebuilder.aspx
You can simply write like:
// begin with true if you start with And operator.
var predicate = PredicateBuilder.True<TblFamilie>();
predicate = predicate.And(t => t.CureDate < DateTime.UtcNow.AddDays(-1));
// you can mix with Or operator too.
predicate = predicate.Or(t => t.Name.Contains("blah"));
var results = context.TblFamilie
.Where(predicate)
.Select(new
{
// your projection here...
});
// begin with false if you start with Or operator.
var predicate2 = PredicateBuilder.False<TblFamilie>();
predicate2 = predicate2.Or(t => t.CureDate < DateTime.UtcNow.AddDays(-1));
// you can mix with And operator too.
predicate2 = predicate2.And(t => t.Name.Contains("blah"));
var results = context.TblFamilie
.Where(predicate)
.Select(new
{
// your projection here...
});
// even nesting is possible
var inner = PredicateBuilder.False<TblFamilie>();
inner = inner.Or (p => p.Name.Contains("foo"));
inner = inner.Or (p => p.Name.Contains("bar"));
var outer = PredicateBuilder.True<TblFamilie>();
outer = outer.And (p => p.CureDate > DateTime.UtcNow.AddDays(-3));
outer = outer.And (p => p.CureDate < DateTime.UtcNow.AddDays(-1));
outer = outer.And (inner);
var results = context.TblFamilie
.Where(outer)
.Select(new
{
// your projection here...
});
Updated
Okay, lets assume you have a Family class, and you get 'Families' from some where. You can use PredicateBuilder like this:
// you have 4 families from DB, API or anywhere.
var failies = new List<Family>
{
new Family { Id = 1, ParentId = 1, Name = "foo", Birthday = new DateTime(1971, 1, 1) },
new Family { Id = 1, ParentId = 1, Name = "bar", Birthday = new DateTime(1982, 1, 1) },
new Family { Id = 1, ParentId = 1, Name = "foobar", Birthday = new DateTime(1993, 1, 1) },
new Family { Id = 1, ParentId = 1, Name = "fake", Birthday = new DateTime(2000, 1, 1) },
};
// make predicate!
// if a family's Birthday is before than 1980 'or' Name contains "ke".
var predicate = PredicateBuilder.True<Family>();
predicate = predicate.And(o => o.Birthday < new DateTime(1980, 1, 1));
predicate = predicate.Or(o => o.Name.Contains("ke"));
// you should make IQueryable in order to use PredicateBuilder.
var result = failies.AsQueryable()
.Where(predicate)
.Select(o => new
{
o.Id, o.Name, o.Birthday // only project what you want.
})
.ToList();
// now, result should contains "foo" and "fake".
foreach (var family in result)
{
Debug.WriteLine("Name: " + family.Name);
}
Updated2
You can copy & paste to LinqPad in order to test how it works. Before you run this in the LinqPad,
Download LinqKit.dll from above link.
Make sure press 'F4' > Add > Browse > select LinqKit.dll > Add LinqKit namespace in the 'Additional Namespace Imports' tab.
In the Query panel, choose Language to 'C# Statement(s)'
paste this and run.
// you have 4 strings from DB, API or anywhere.
var strings = new List<string>
{
"foo",
"bar",
"foobar",
"fake"
};
// make predicate!
// if a string contains "oo" or "ke"
var predicate = PredicateBuilder.True<string>();
predicate = predicate.And(o => o.Contains("oo"));
predicate = predicate.Or(o => o.Contains("ke"));
// you should make IQueryable in order to use PredicateBuilder.
var result = strings.AsQueryable()
.Where(predicate)
.ToList();
// now, result should contains "foo", "foobar" and "fake".
foreach (var stringResult in result)
{
Debug.WriteLine("Name: " + stringResult);
}

Select Single Element from Jagged Array

I'm working on a problem that's making my brain melt although I don't think it should be this hard. My example is long so I'll try to keep my question short!
I have an Array object that contains some elements that are also Arrays. For example:
customerAddresses = new customer_address[]
{
new // address #1
{
customer_id = 6676979,
customer_address_seq = 1,
customer_address_match_codes = new []
{
new
{
customer_address_seq = 1,
customer_id = 6676979,
customer_match_code_id = 5
}
}
},
new // address #2
{
customer_id = 6677070,
customer_address_seq = 1,
customer_address_match_codes = new []
{
new
{
customer_address_seq = 1,
customer_id = 6677070,
customer_match_code_id = 4
},
new
{
customer_address_seq = 1,
customer_id = 6677070,
customer_match_code_id = 5
},
new
{
customer_address_seq = 1,
customer_id = 6677070,
customer_match_code_id = 3
}
}
},
new // address #3
{
customer_id = 6677070,
customer_address_seq = 2,
customer_address_match_code = new []
{
new
{
customer_address_seq = 2,
customer_id = 6677070,
customer_match_code_id = 4
},
new
{
customer_address_seq = 2,
customer_id = 6677070,
customer_match_code_id = 5
}
}
}
};
As you can see, the Array contains a number of address records, with one record per combination of customer_id and customer_address_seq. What I'm trying to do is find the best matching customer_address according to the following rules:
There must be customer_match_code_id equal to 4 and there must be one equal to 5
If there is a customer_match_code_id equal to 3, then consider that customer_address a stronger match.
According to the above rules, the 2nd customer_address element is the "best match". However, the last bit of complexity in this problem is that there could be multiple "best matches". How I need to handle that situation is by taking the customer_address record with the minimum customer_id and minimum customer_address_seq.
I was thinking that using LINQ would be my best bet, but I'm not experienced enough with it, so I just keep spinning my wheels.
Had to make a change to your class so that you are actually assigning your one collection to something:
customer_address_match_codes = new customer_address_match_code[]
{
new
{
customer_address_seq = 1,
customer_id = 6676979,
customer_match_code_id = 5
}
}
And then here is the LINQ that I've tested and does what you specify:
var result = (from c in customerAddresses
let isMatch = c.customer_address_match_codes
.Where (cu => cu.customer_match_code_id == 4).Any () &&
c.customer_address_match_codes
.Where (cu => cu.customer_match_code_id == 5).Any ()
let betterMatch = isMatch && c.customer_address_match_codes
.Where (cu => cu.customer_match_code_id == 3).Any () ? 1 : 0
where isMatch == true
orderby betterMatch descending, c.customer_id, c.customer_address_seq
select c)
.FirstOrDefault ();
I've worked up an example using your data with anonymous types here: http://ideone.com/wyteM
Not tested and not the same names but this should get you going
customer cb = null;
customer[] cs = new customer[] {new customer()};
foreach (customer c in cs.OrderBy(x => x.id).ThenBy(y => y.seq))
{
if(c.addrs.Any(x => x.num == "5"))
{
if(c.addrs.Any(x => x.num == "3"))
{
if (cb == null) cb = c;
if (c.addrs.Any(x => x.num == "2"))
{
cb = c;
break;
}
}
}
}
This sounds like a job for LINQ
var bestMatch = (from address in DATA
where address.customer_address_match_code.Any(
x => x.customer_match_code_id == 4)
where address.customer_address_match_code.Any(
x => x.customer_match_code_id == 5)
select address).OrderBy(
x => x.customer_address_match_code.Where(
y => y.customer_match_code_id >= 3)
.OrderBy(y => y.customer_match_code_id)
.First()
.customer_match_code_id).FirstOrDefault();
My theory is this: Select addresses that have both a customer_match_code_id == 4 and a customer_match_code_id == 5. Then sort them by the the lowest customer_match_code_id they have that are at least 3, and then take the very first one. If there are a customer_match_code_id that equals 3 then that one is selected, if not, some else is selected. If nothing matches both 4 and 5 then null is returned.
Untested.
Seems quite straight forward in LINQ:
var query =
from ca in customerAddresses
where ca.customer_address_match_codes.Any(
mc => mc.customer_match_code_id == 4)
where ca.customer_address_match_codes.Any(
mc => mc.customer_match_code_id == 5)
orderby ca.customer_id
orderby ca.customer_address_seq
orderby ca.customer_address_match_codes.Any(
mc => mc.customer_match_code_id == 3) descending
select ca;
var result = query.Take(1);
How does that look?

Assign values from one list to another using LINQ

Hello I have a little problem with assigning property values from one lists items to anothers. I know i could solve it "the old way" by iterating through both lists etc. but I am looking for more elegant solution using LINQ.
Let's start with the code ...
class SourceType
{
public int Id;
public string Name;
// other properties
}
class DestinationType
{
public int Id;
public string Name;
// other properties
}
List<SourceType> sourceList = new List<SourceType>();
sourceList.Add(new SourceType { Id = 1, Name = "1111" });
sourceList.Add(new SourceType { Id = 2, Name = "2222" });
sourceList.Add(new SourceType { Id = 3, Name = "3333" });
sourceList.Add(new SourceType { Id = 5, Name = "5555" });
List<DestinationType> destinationList = new List<DestinationType>();
destinationList.Add(new DestinationType { Id = 1, Name = null });
destinationList.Add(new DestinationType { Id = 2, Name = null });
destinationList.Add(new DestinationType { Id = 3, Name = null });
destinationList.Add(new DestinationType { Id = 4, Name = null });
I would like to achieve the following:
destinationList should be filled with Names of corresponding entries (by Id) in sourceList
destinationList should not contain entries that are not present in both lists at once (eg. Id: 4,5 should be eliminated) - something like inner join
I would like to avoid creating new destinationList with updated entries because both lists already exist and are very large,
so no "convert" or "select new".
In the end destinationList should contain:
1 "1111"
2 "2222"
3 "3333"
Is there some kind of elegant (one line Lambda? ;) solution to this using LINQ ?
Any help will be greatly appreciated! Thanks!
I would just build up a dictionary and use that:
Dictionary<int, string> map = sourceList.ToDictionary(x => x.Id, x => x.Name);
foreach (var item in destinationList)
if (map.ContainsKey(item.Id))
item.Name = map[item.Id];
destinationList.RemoveAll(x=> x.Name == null);
Hope this will your desired result. First join two list based on key(Id) and then set property value from sourceList.
var result = destinationList.Join(sourceList, d => d.Id, s => s.Id, (d, s) =>
{
d.Name = s.Name;
return d;
}).ToList();
Barring the last requirement of "avoid creating new destinationList" this should work
var newList = destinationList.Join(sourceList, d => d.Id, s => s.Id, (d, s) => s);
To take care of "avoid creating new destinationList", below can be used, which is not any different than looping thru whole list, except that it probably is less verbose.
destinationList.ForEach(d => {
var si = sourceList
.Where(s => s.Id == d.Id)
.FirstOrDefault();
d.Name = si != null ? si.Name : "";
});
destinationList.RemoveAll(d => string.IsNullOrEmpty(d.Name));
Frankly, this is the simplest:
var dictionary = sourceList.ToDictionary(x => x.Id, x => x.Name);
foreach(var item in desitnationList) {
if(dictionary.ContainsKey(item.Id)) {
item.Name = dictionary[item.Id];
}
}
destinationList = destinationList.Where(x => x.Name != null).ToList();
You could do something ugly with Join but I wouldn't bother.
I hope this will be useful for you. At the end, destinationList has the correct data, without creating any new list of any kind.
destinationList.ForEach(x =>
{
SourceType newSource = sourceList.Find(s=>s.Id == x.Id);
if (newSource == null)
{
destinationList.Remove(destinationList.Find(d => d.Id == x.Id));
}
else
{
x.Name = newSource.Name;
}
});

Categories