Using Linq to check if generic list in Dictionary contains a value - linq

How do I check if a dictionary already contains a set of coordinates? I am using SelectManager and Where but the count is still wrong. I don't think its checked the values in the the generic list.
I want it to iterate through the matchTile and check every other elements in the generic list for a condition (not part of question).
If it meets that condition, it get adds it to the same group. When the next iteration comes, it should check if that element has already been added to a group. If yes, it skips it. If not, it creates a new group (List).
Can anyone help?
private void groupMatches(List<int[]> matchTile){
Dictionary<int, List<int[]>> groups = new Dictionary<int, List<int[]>>();
int i = 0;
foreach(int[] coord in matchTile){
var alreadyCounted = groups.SelectMany(x => x.Value).Where(x => x[0] == coord[0] && x[1] == coord[1]);
Debug.Log ("counted: " + alreadyCounted.Count ());
if(alreadyCounted.Count() > 0) continue;
// Create new group
groups.Add(i, new List<int[]>());
groups[i].Add(coord);
foreach(int[] nextCoord in matchTile){
if (...)
{
groups[i].Add(nextCoord);
}
}
i++;
}
Debug.Log ("groups: " + groups.Count);
}

Try next code:
private void groupMatches(List<int[]> matchTile)
{
Dictionary<int, List<int[]>> groups
= matchTile.GroupBy(line => new{x = line[0], y = line[1]})
.Select((grp, index) => new{index, grp})
.ToDictionary(info => info.index, info => info.grp.ToList());
Debug.Log("groups: " + groups.Count);
}

Related

add data to an arrayList, when If condition is satisfied in nested for loop in Kotlin

Check the code below:
getCommonsArrayList(listA:ArrayList< User >, listB:ArrayList<User>):ArrayList<User>{
var listCommon = ArrayList<User>()
for (i in listA.indices) {
for (j in listB.indices) {
if (listA[i].id.equals(listB[j].id)) { //if id of the user matches
listCommon.put(listA[i]) //add to a new list
}
}
}
return listCommon // return the new list with common entries
}
The above method iterates list a & b and check whether the id's are matching, if they are then the User object is stored to a new list and at the end of the program, it returns the common list.
This thing works good. And I hope nested for followed by if condition is the way in which we can compare two lists.
The problem with this is if listA has repeated entries, then the listCommon will also have repeated entries as ArrayList supports duplicacy of entries.
So what I did to make commonList unique is I introduced a HashMap object as shown below:
getCommonsArrayList(listA:ArrayList< User >, listB:ArrayList<User>):ArrayList<User>{
var listCommon = ArrayList<User>()
var arrResponseMap = HashMap<String,User>()
for (i in listA.indices) {
for (j in listB.indices) {
if (listA[i].id.equals(listB[j].id)) { //if id of the user matches
arrResponseMap.put(listA[i].id,listA[i]) // add id to map so there would be no duplicacy
}
}
}
arrResponseMap.forEach {
listCommon.add(it.value) //iterate the map and add all values
}
return listCommon // return the new list with common entries
}
This will give the new arrayList of userObject with common Id's. But this has an increased complexity than the above code.
If the size of the listA and listB increases to 1000 then this execution will take heavy time.
Can someone guide me if there is some better way to solve this.
You can simply use distinctBy to get only unique values from list.
Official Doc:
Returns a sequence containing only elements from the given sequence
having distinct keys returned by the given selector function.
The elements in the resulting sequence are in the same order as they
were in the source sequence.
Here is an example:
val model1 = UserModel()
model1.userId = 1
val model2 = UserModel()
model1.userId = 2
val model3 = UserModel()
model1.userId = 1
val model4 = UserModel()
model1.userId = 2
val commonList = listOf(model1, model2, model3, model4)
// get unique list based on userID, use any field to base your distinction
val uniqueList = commonList
.distinctBy { it.userId }
.toList()
assert(uniqueList.count() == 2)
assert(commonList.count() == 4)
Add the both the list and use distinctBy like this
data class DevelopersDetail(val domain: String, val role: String)
val d1 = DevelopersDetail("a", "1")
val d2 = DevelopersDetail("b", "1")
val d3 = DevelopersDetail("c", "1")
val d4 = DevelopersDetail("c", "1")
val d5 = DevelopersDetail("d", "1")
var listA = listOf(d1, d2, d3, d4)
var listb = listOf(d1, d2, d3, d4)
var data = listA + listb
var list= data
.distinctBy { it.domain }
.toList()
println("list $list")
//output-list [DevelopersDetail(domain=a, role=1), DevelopersDetail(domain=b, role=1), DevelopersDetail(domain=c, role=1)]

filter elements in nested dictionary LINQ

I have the following data structure:
Dictionary<string, Dictionary<string, List<int>>> data =
new Dictionary<string, Dictionary<string, List<int>>>();
I want to filter some of the elements in that dictionary based on value in first element of the list of the inner dictionary.
for example:
{legion1
{soldier1, [10,1000]},
{soldier2, [50,1000]}
}
Now let's say I want to do foreach loop in which to work only elements where
the value of the first element of the list is less than 20
expected result in the foreach loop is:
{legion1{soldier1, [10,1000]}}
What I've tried:
I do foreach loop and then I want to use something similar:
data.where(x => x.value.where(o => o[0] < 20 ))
I always get error that that way is incorrect.
Please tell how can I solve the issue and why my way is failing.
You can filter and iterate over the result set like so:
var resultSet =
data.ToDictionary(e => e.Key,
e => e.Value.Where(x => x.Value[0] < 20)
.ToDictionary(k => k.Key, v => v.Value)
);
foreach(var item in resultSet){
var key = item.Key; // string
var values = item.Value; // Dictionary<string, List<int>>
...
...
}
The problem is that you are applying operator [] incorrectly. Moreover, since you want to use both Legion and Soldier, you should construct a tuple combining the two of them:
foreach (var t in data.SelectMany(lg => lg.Value.Select(s => new {
Legion = lg
, Soldier = s
})).Where(ls => ls.Soldier.Value[0] < 20)) {
Console.WriteLine("Legion={0} Soldier = {1}", t.Legion.Key, t.Soldier.Key);
}

linq compare with previous item

I need to filter a list, by removing all items have the same language code as the item before (the items are orderd by time). This way, I want to detect all border crossings.
Is there a way to do this with one line of linq?
var test = new List<Sample> { new Sample("AT", "test1"),
new Sample("AT", "test2") ,
new Sample("AT", "test3") ,
new Sample("DE", "test4") ,
new Sample("DE", "test5") ,
new Sample("DE", "test6") ,
new Sample("AT", "test7") ,
new Sample("AT", "test8") ,
new Sample("AT", "test9")
};
var borderChanges = new List<Sample>();
var lastCountry = "";
foreach (var sample in test)
{
if (sample.country != lastCountry)
{
lastCountry = sample.country;
borderChanges.Add(sample);
}
}
I think this is what are you looking for:
test.Where((x,idx) => idx == 0 || x.Country != test[idx - 1].Country));
In case someone else is curious to see, how this problem can be solved with Aggregate(), here is the answer:
var borderChanges = test.Take(1).ToList();
test.Aggregate((last, curr) =>
{
if (last.Code != curr.Code)
{
borderChanges.Add(curr);
}
return curr;
});
Aggregate() executes the lambda expression for every element in the list, except the first. So we initialize the list with the first item.
In the lamda we add all items where the current item doesnt equal the aggregated value. In our case the aggredated value is always the previously checked item.
In the foreach iteration, access to neighboring elements of the sequence is impossible. Perhaps your goal can be achieved with the Aggregate extension method for IEnumerable<T>, where you can compare neighboring elements of the sequence.

Row number in LINQ

I have a linq query like this:
var accounts =
from account in context.Accounts
from guranteer in account.Gurantors
where guranteer.GuarantorRegistryId == guranteerRegistryId
select new AccountsReport
{
recordIndex = ?
CreditRegistryId = account.CreditRegistryId,
AccountNumber = account.AccountNo,
}
I want to populate recordIndex with the value of current row number in collection returned by the LINQ. How can I get row number ?
Row number is not supported in linq-to-entities. You must first retrieve records from database without row number and then add row number by linq-to-objects. Something like:
var accounts =
(from account in context.Accounts
from guranteer in account.Gurantors
where guranteer.GuarantorRegistryId == guranteerRegistryId
select new
{
CreditRegistryId = account.CreditRegistryId,
AccountNumber = account.AccountNo,
})
.AsEnumerable() // Moving to linq-to-objects
.Select((r, i) => new AccountReport
{
RecordIndex = i,
CreditRegistryId = r.CreditRegistryId,
AccountNumber = r.AccountNo,
});
LINQ to objects has this builtin for any enumerator:
http://weblogs.asp.net/fmarguerie/archive/2008/11/10/using-the-select-linq-query-operator-with-indexes.aspx
Edit: Although IQueryable supports it too (here and here) it has been mentioned that this does unfortunately not work for LINQ to SQL/Entities.
new []{"aap", "noot", "mies"}
.Select( (element, index) => new { element, index });
Will result in:
{ { element = aap, index = 0 },
{ element = noot, index = 1 },
{ element = mies, index = 2 } }
There are other LINQ Extension methods (like .Where) with the extra index parameter overload
Try using let like this:
int[] ints = new[] { 1, 2, 3, 4, 5 };
int counter = 0;
var result = from i in ints
where i % 2 == 0
let number = ++counter
select new { I = i, Number = number };
foreach (var r in result)
{
Console.WriteLine(r.Number + ": " + r.I);
}
I cannot test it with actual LINQ to SQL or Entity Framework right now. Note that the above code will retain the value of the counter between multiple executions of the query.
If this is not supported with your specific provider you can always foreach (thus forcing the execution of the query) and assign the number manually in code.
Because the query inside the question filters by a single id, I think the answers given wont help out. Ofcourse you can do it all in memory client side, but depending how large the dataset is, and whether network is involved, this could be an issue.
If you need a SQL ROW_NUMBER [..] OVER [..] equivalent, the only way I know is to create a view in your SQL server and query against that.
This Tested and Works:
Amend your code as follows:
int counter = 0;
var accounts =
from account in context.Accounts
from guranteer in account.Gurantors
where guranteer.GuarantorRegistryId == guranteerRegistryId
select new AccountsReport
{
recordIndex = counter++
CreditRegistryId = account.CreditRegistryId,
AccountNumber = account.AccountNo,
}
Hope this helps.. Though its late:)

How to join results from two different sets in LINQ?

I get some data about customers in my database with this method:
public List<KlientViewModel> GetListOfKlientViewModel()
{
List<KlientViewModel> list = _klientRepository.List().Select(k => new KlientViewModel
{
Id = k.Id,
Imie = k.Imie,
Nazwisko = k.Nazwisko,
Nazwa = k.Nazwa,
SposobPlatnosci = k.SposobPlatnosci,
}).ToList();
return list;
}
but also I have another method which counts value for extra field in KlientViewModel - field called 'Naleznosci'.
I have another method which counts value for this field based on customers ids, it looks like this:
public Dictionary<int, decimal> GetNaleznosc(List<int> klientIds)
{
return klientIds.ToDictionary(klientId => klientId, klientId => (from z in _zdarzenieRepository.List()
from c in z.Klient.Cennik
where z.TypZdarzenia == (int) TypyZdarzen.Sprzedaz && z.IdTowar == c.IdTowar && z.Sprzedaz.Data >= c.Od && (z.Sprzedaz.Data < c.Do || c.Do == null) && z.Klient.Id == klientId
select z.Ilosc*(z.Kwota > 0 ? z.Kwota : c.Cena)).Sum() ?? 0);
}
So what I want to do is to join data from method GetNaleznosc with data generated in method GetListOfKlientViewModel. I call GetNaleznosc like this:
GetNaleznosc(list.Select(k => k.Id).ToList())
but don't know what to do next.
Having obtained the dictionary:
var dict = GetNaleznosc(list.Select(k => k.Id).ToList());
You can now efficiently look up the decimal value of Naleznosci for a given client:
foreach (var k in list)
k.Naleznosci = dict[k.Id];
Now you have merged the values into the main list. Is this what you mean?
By the way, in your function that builds the dictionary, you make it accept a List<int>, but then all you do is call ToDictionary on it, which only requires IEnumerable<int>. So change the parameter type to that, and then you can call it:
var dict = GetNaleznosc(list.Select(k => k.Id));
This removes the call to ToList, which avoids making an unnecessary intermediate copy of the whole list of Ids. Probably won't make much difference in this case if you're hitting a database and then building up a large set of results in memory, but maybe worth bearing in mind for other uses of these operations.
Also, looking again at the helper function, there is no apparent advantage in building up the set of results in a dictionary for a list of ids, because each one is handled independently. You could simply put:
public decimal GetNaleznosc(int klientId)
{
return (from z in _zdarzenieRepository.List()
from c in z.Klient.Cennik
where z.TypZdarzenia == (int) TypyZdarzen.Sprzedaz && z.IdTowar == c.IdTowar && z.Sprzedaz.Data >= c.Od && (z.Sprzedaz.Data < c.Do || c.Do == null) && z.Klient.Id == klientId
select z.Ilosc*(z.Kwota > 0 ? z.Kwota : c.Cena)).Sum() ?? 0);
}
That is, provide a function that discovers just one value. Now you can directly build the right list:
public List<KlientViewModel> GetListOfKlientViewModel()
{
return _klientRepository.List().AsEnumerable().Select(k => new KlientViewModel
{
Id = k.Id,
Imie = k.Imie,
Nazwisko = k.Nazwisko,
Nazwa = k.Nazwa,
SposobPlatnosci = k.SposobPlatnosci,
Naleznosci = GetNaleznosc(k.Id)
}).ToList();
}

Resources