How to choose certain values from ICollection to IDictionary, using LINQ - c#

I have IDictionary<int,bool?> where int - id, bool? - state (true,false,null)
So i need to filter ICollection of objects, where i should compare internal id with id of my IDictionary and if IDs are the same and the state is true - i should select this element (using LINQ)
I tried: incomeCollection.Values.Select(x=>x.InternalId.Equals(dataFromDictionary.Keys.Any)).Select(h=> new Item){Item = h.Name}
but it does not works. I need to check all collection and select elements, which satisfy the condition above using LINQ. How can i do this?

The dictionary has a handy TryGetValue method allowing to look up an entry quickly.
class Income
{
public int InternalId { get; set; }
public string Name { get; set; }
}
var dictionary = new Dictionary<int,bool?>
{
{1, false},
{2, true},
{3, null},
};
var incomeCollection = new List<Income>
{
new Income { InternalId = 1, Name = "A" },
new Income { InternalId = 2, Name = "B" },
new Income { InternalId = 3, Name = "C" },
new Income { InternalId = 4, Name = "D" },
};
var result = incomeCollection.Where(x =>
dictionary.TryGetValue(x.InternalId, out var status) && status == true)
.Select(h=> new {Item = h.Name});
This is better than your first approach using dataFromDictionary.Keys.Any which does not take advantage of the Dictionary feature of quick lookup.

You can use Any()
var result = list.Where(x=>dictionary.Keys.Any(c=>c.Equals(x.Id)) && x.State.HasValue && x.State==true).Select(x=>x);
You can also use Join.
var result = list.Join(dictionary,
l=>l.Id,
d=>d.Key,(l,d)=>l)
.Where(x=>x.State.HasValue && x.State==true).Select(x=>x);
For example,
var dictionary = new Dictionary<int,bool?>
{
[1] = true,
[3] = true,
[35] = false
};
var list = new List<Person>
{
new Person{Name="Jia", Id=1,State=false},
new Person{Name="Aami", Id=3,State=true},
new Person{Name="Anu", Id=35,State=null},
};
Output
Aami 3 True

ToDictionary method can be used as shown in the below example.
In order to select only few values from ICollection you can use where clause.
List<Package> packages =
new List<Package>
{ new Package { Company = "Coho Vineyard", Weight = 25.2, TrackingNumber = 89453312L },
new Package { Company = "Lucerne Publishing", Weight = 18.7, TrackingNumber = 89112755L },
new Package { Company = "Wingtip Toys", Weight = 6.0, TrackingNumber = 299456122L },
new Package { Company = "Adventure Works", Weight = 33.8, TrackingNumber = 4665518773L } };
// Create a Dictionary of Package objects,
// using TrackingNumber as the key.
Dictionary<long, Package> dictionary =
packages.ToDictionary(p => p.TrackingNumber);
foreach (KeyValuePair<long, Package> kvp in dictionary)
{
Console.WriteLine(
"Key {0}: {1}, {2} pounds",
kvp.Key,
kvp.Value.Company,
kvp.Value.Weight);
}

Related

I can not retrieve a list of objects from CosmosDb document

I made a mistake with my SQL query with ComsosDb .NET SDK 3. I want to request a list of objects from a document.
This is my document as stored in CosmosDb (fid is partition key):
{
"id": "1abc",
"fid": "test",
"docType": "Peeps",
"People": [
{
"Name": "Bill",
"Age": 99
},
{
"Name": "Marion",
"Age": 98
},
{
"Name": "Seb",
"Age": 97
}
],
"_rid": "mo53AJczKUuL9gMAAAAAAA==",
"_self": "dbs/mo53AA==/colls/mo53AJczKUs=/docs/mo53AJczKUuL9gMAAAAAAA==/",
"_etag": "\"9001cbc7-0000-1100-0000-60c9d58d0000\"",
"_attachments": "attachments/",
"_ts": 1623840141
}
My results show an item count of 1 with properties set to default values - Name is null and Age is 0.
I was expecting a IEnumerable of 3 persons. Here is the code:
class MyPeople
{
public IEnumerable<Person> People { get; set; }
}
class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
[Fact]
public async Task CosmosPeopleTest_ReturnsThreePeople()
{
var config = GetConFig();
var cosmosClientV2 = new CosmosClient(config["Cosmos:ConnectionString"]);
var container = cosmosClientV2.GetContainer(config["Cosmos:DbName"], config["Cosmos:Collectionname"]);
var sql = "SELECT c.People FROM c WHERE c.docType = 'Peeps'";
QueryDefinition queryDefinition = new QueryDefinition(sql);
var results = new List<Person>();
FeedIterator<Person> q = container.GetItemQueryIterator<Person>(queryDefinition, null, new QueryRequestOptions { PartitionKey = new PartitionKey("test") });
while (q.HasMoreResults)
{
var x = await q.ReadNextAsync();
results.AddRange(x.ToList());
}
Assert.Equal(3, results.Count);
}
If I change the query to
sql = "SELECT c.People FROM c JOIN d IN c.People";
I have three Person all with properties Name & Age which are defaults.
You have an issue with the types. SELECT c.People return an object in this form:
{
People: [
...
]
}
When you iterate with this code
FeedIterator<Person> q = container.GetItemQueryIterator<Person>(queryDefinition, null, new QueryRequestOptions { PartitionKey = new PartitionKey("test") });
The CosmosDB try to "convert" every result object (as above) to a Person class. But it using reflection for that. So it creates a Person object but doesn't find any fields to fill its properties - it will not fail, but create empty objects with all the properties initialized with default values.
So to solve it you need to use MyPeople instead of Person:
FeedIterator<MyPeople> q = container.GetItemQueryIterator<MyPeople>(queryDefinition, null, new QueryRequestOptions { PartitionKey = new PartitionKey("test") });
Since MyPeople is the right form of the returned object, it will be able to read the objects that the CosmosDB returns and use them.
The full working code:
var config = GetConFig();
var cosmosClientV2 = new CosmosClient(config["Cosmos:ConnectionString"]);
var container = cosmosClientV2.GetContainer(config["Cosmos:DbName"], config["Cosmos:Collectionname"]);
var sql = "SELECT c.People FROM c WHERE c.docType = 'Peeps'";
QueryDefinition queryDefinition = new QueryDefinition(sql);
var results = new List<Person>();
FeedIterator<MyPeople> q = container.GetItemQueryIterator<MyPeople>(queryDefinition, null, new QueryRequestOptions { PartitionKey = new PartitionKey("test") });
while (q.HasMoreResults)
{
var x = await q.ReadNextAsync();
var myPeopleRes = x.Resource;
foreach (var people in myPeopleRes)
{
results.AddRange(people.People);
}
}
Assert.Equal(3, results.Count);

How do I update Dictionary values (object) irrespective of key

I have a dictionary which has more than 2000+ elements. I am trying to update an element in the value based on a condition. How do I do that in a simple statement instead of looping through?
Dictionary<int, MyObj> testDictionary = new Dictionary<int, MyObj>();
testDictionary.Add(1,new MyObj() {Name = "John",Code = "JN",Id = null});
testDictionary.Add(2,new MyObj() {Name = "John",Code = "JN",Id = null});
testDictionary.Add(3,new MyObj() {Name = "Champ",Code = "CP",Id = null});
testDictionary.Add(4,new MyObj() {Name = "SMITH",Code = "SH",Id = null});
testDictionary.Add(5,new MyObj() {Name = "SMITH",Code = "SH",Id = null});
Dictionary<int, MyObj>
public class MyObj
{
public string Name { get; set; }
public string Code { get; set; }
public int? Id { get; set; }
}
How do I update all of Dictionary values (MyObj)
where Code = JN and Name = John with 100
where Code = SH and Name = SMITH with 989
I can get matched list using below statement but do I ressign to dictionary?
var enList = testDictionary.Values.Where(d => d.Name == "John" && d.Code == "JN");
Try the code bellow, this change the value of name to test attribute in any item where the value of Name is "SMITH".
testDictionary.Where(y => y.Value.Name == "SMITH")
.Select(r => r.Value)
.ToList<MyObj>()
.ForEach(k => k.Name = "test");
This line does not return result, the value is changed directly in the textDictionary instance.
Look at the example - you don't need to reassign values in the Dictionary, they are stored as references.
var enList = testDictionary.Values.Where(d => d.Name == "John" && d.Code == "JN").ToList();
foreach (var item in enList)
{
item.Name = "Updated Name";
}
Console.WriteLine(testDictionary[1].Name);

Combining two Lists of an Object into one

I am currently developing an application that requires this senario.
Assuming I have this object
public class ObjectItem
{
public int Id {get;set;}
public int Name {get;set;}
public int Sex {get;set;}
public int Age {get;set;}
public string Complexion {get;set;}
}
If we now have two lists of this object
var studentWithAge = new List<ObjectItem>
{
new ObjectItem {Id = 1, Name = "John", Age = 2},
new ObjectItem {Id = 2, Name = "Smith", Age = 5},
new ObjectItem {Id = 3, Name = "Juliet", Age = 7},
};
var studentWithSexAndComplexion = new List<ObjectItem>
{
new ObjectItem {Id = 1, Name = "John", Sex = "Male", Complexion = "fair"},
new ObjectItem {Id = 2, Name = "Smith", Sex = "Male", Complexion = " "},
new ObjectItem {Id = 3, Name = "Juliet", Sex = "Female", Complexion = "Blonde"},
new ObjectItem {Id = 4, Name = "Shittu", Sex = "Male", Complexion = "fair"},
};
I want to merge these two lists into just one. The end result should look like this.
var CompleteStudentData=new List<ObjectItem>
{
new ObjectItem{Id=1,Name="John",Sex="Male", Complexion="fair",Age=2},
new ObjectItem{Id=2,Name="Smith",Sex="Male", Complexion=" ", Age=5},
new ObjectItem{Id=3,Name="Juliet",Sex="Female", Complexion="Blonde", Age=7},
new ObjectItem{Id=4,Name="Shittu",Sex="Male", Complexion="fair", Age=0},
}
How do i achieve this? Using Union to merge the two list does not produce the desired result. I would appreciate your help.
var result = StudentWithAge.Join(StudentWithSexAndComplexion,
sa => sa.Id,
ssc => ssc.Id,
(sa, ssc) => new ObjectItem
{
Id = sa.Id,
Name = sa.Name,
Age = sa.Age,
Sex = ssc.Sex,
Complexion = ssc.Complexion
}).ToList();
Or, avoiding creation of new objects:
var result = StudentWithAge.Join(StudentWithSexAndComplexion,
sa => sa.Id,
ssc => ssc.Id,
(sa, ssc) =>
{
sa.Sex = ssc.Sex;
sa.Complexion = ssc.Complexion;
return sa;
}).ToList();
And if you want to add students presented only in the second list, than also:
result.AddRange(StudentWithSexAndComplexion.Where(ssc => !StudentWithAge.Any(sa => sa.Id == ssc.Id)));
Since it's possible that your collections will not have a 1-to-1 correspondence, you would have to do a full outer join. See here for how you can compose it that way.
Here's one way you can get similar results.
Collect all the keys (the ids) from both collections, then perform a left join with each of the collections, then combine the results.
var ids = studentWithAge.Select(s => s.Id)
.Union(studentWithSexAndComplexion.Select(s => s.Id));
var query =
from id in ids
from sa in studentWithAge
.Where(sa => sa.Id == id)
.DefaultIfEmpty(new ObjectItem { Id = id })
from ssc in studentWithSexAndComplexion
.Where(ssc => ssc.Id == id)
.DefaultIfEmpty(new ObjectItem { Id = id })
select new ObjectItem
{
Id = id,
Name = sa.Name ?? ssc.Name,
Sex = ssc.Sex,
Age = sa.Age,
Complexion = ssc.Complexion,
};
.Net has a function which is concatenating collections:
var concatenatedCollection = StudentWithAge.Concat(StudentWithSexAndComplexion).ToList();
var StudentWithAge = new List<ObjectItem>()
{
new ObjectItem{Id=1,Name="John",Age=2},
new ObjectItem{Id=2,Name="Smith",Age=5},
new ObjectItem{Id=3,Name="Juliet",Age=7},
};
var StudentWithSexAndComplexion = new List<ObjectItem>()
{
new ObjectItem{Id=1,Name="John",Sex="Male", Complexion="fair"},
new ObjectItem{Id=2,Name="Smith",Sex="Male", Complexion=" "},
new ObjectItem{Id=3,Name="Juliet",Sex="Female", Complexion="Blonde"},
new ObjectItem{Id=4,Name="Shittu",Sex="Male", Complexion="fair"},
};
var concatenatedCollection = StudentWithAge.Concat(StudentWithSexAndComplexion).ToList();

Linq query for searching an object inside a dictionary having list of lists as values

I'm searching a sorted dictionary with a key of type datetime and values as list of objects. What I need to find is the latest value(based on a property on the object) for each object in the dictionary. My object has 3 properties : a name, a value and a date when it was created. My dictionary is sorted by latest date in descending order.
I have got this working somehow, but I'm sure there is a shortcut for this using LINQ. Please note that I'm using .NET 3.5. Could you please help? Please dont get put off by the huge amount code below as I have added it for clarity and i'm only looking for a linq query to query inside a list of list objects.
Code below:
public void Should_link_recent_data_together()
{
var data = TimeSeriesDataFactoryEx.GetData();
var allAttributes = new List<string>()
{
TimeSeriesConstants.TOTAL_COST_CODE,
TimeSeriesConstants.TOTAL_VALUE_CODE,
TimeSeriesConstants.SOURCE_CODE
};
var latestList = new List<TimeSeries>();
var allValues = data.Values.ToList();
#region HOW DO I DO THIS USING LINQ?
bool found = false;
foreach (var attribute in allAttributes)
{
found = false;
foreach (var tsData in allValues)
{
foreach (var ts in tsData)
{
if (ts.MetricName == attribute && !string.IsNullOrEmpty(ts.MetricValue))
{
latestList.Add(ts);
found = true;
break;
}
}
if (found)
break;
}
}
#endregion
Assert.IsTrue(latestList.Count == 3);
Assert.IsTrue(latestList.Where(x => x.MetricName == TimeSeriesConstants.TOTAL_COST_CODE).First().MetricValue == "1");
Assert.IsTrue(latestList.Where(x => x.MetricName == TimeSeriesConstants.TOTAL_VALUE_CODE).First().MetricValue == "2");
Assert.IsTrue(latestList.Where(x => x.MetricName == TimeSeriesConstants.SOURCE_CODE).First().MetricValue == "gp");
Assert.IsTrue(latestList.Where(x => x.MetricName == TimeSeriesConstants.SOURCE_CODE).First().Quarter == DateTime.Today.AddMonths(-3));
}
internal class TimeSeriesDataFactoryEx
{
public static SortedDictionary<DateTime?,List<TimeSeries>> GetData()
{
return new SortedDictionary<DateTime?, List<TimeSeries>>(new DateComparer())
{
{
DateTime.Today, new List<TimeSeries>()
{
new TimeSeries()
{
Quarter = DateTime.Today,
MetricValue = "1",
MetricName = TimeSeriesConstants.TOTAL_COST_CODE
},
new TimeSeries()
{
Quarter = DateTime.Today,
MetricValue = "2",
MetricName = TimeSeriesConstants.TOTAL_VALUE_CODE
},
new TimeSeries()
{
Quarter = DateTime.Today,
MetricValue = "",
MetricName = TimeSeriesConstants.SOURCE_CODE
}
}
},
{
DateTime.Today.AddMonths(-3), new List<TimeSeries>()
{
new TimeSeries()
{
Quarter = DateTime.Today.AddMonths(-3),
MetricValue = "3",
MetricName = TimeSeriesConstants.TOTAL_COST_CODE
},
new TimeSeries()
{
Quarter = DateTime.Today.AddMonths(-3),
MetricValue = "4",
MetricName = TimeSeriesConstants.TOTAL_VALUE_CODE
},
new TimeSeries()
{
Quarter = DateTime.Today.AddMonths(-3),
MetricValue = "gp",
MetricName =TimeSeriesConstants.SOURCE_CODE
}
}
}
};
}
}
So, assuming I understand your question right, say you have a dictionary like so:
{ Key = "1/1/1900", Value = List Of Objects, of which each has a DateTimeProperty }
...
{ Key = "1/4/1900", Value = List Of Objects, of which each has a DateTimeProperty }
And you are looking to find a set of objects from your dictionary, where it's the latest by time of each key, then you can do this pretty simply with linq:
var latestItems = data.SelectMany(kvp =>
kvp.Value.OrderByDescending(value => value.Quarter).Take(1)
);
This query finds the most recent object in each bucket and then returns that as a single set (not an enumerable of enumerables). You can change the selector inside the SelectMany to find elements in each set as much as you want, as long as you return an IEnumerable from that callback.

Dynamic Linq Groupby SELECT Key, List<T>

I am using Dynamic Linq helper for grouping data. My code is as follows :
Employee[] empList = new Employee[6];
empList[0] = new Employee() { Name = "CA", State = "A", Department = "xyz" };
empList[1] = new Employee() { Name = "ZP", State = "B", Department = "xyz" };
empList[2] = new Employee() { Name = "AC", State = "B", Department = "xyz" };
empList[3] = new Employee() { Name = "AA", State = "A", Department = "xyz" };
empList[4] = new Employee() { Name = "A2", State = "A", Department = "pqr" };
empList[5] = new Employee() { Name = "BA", State = "B", Department = "pqr" };
var empqueryable = empList.AsQueryable();
var dynamiclinqquery = DynamicQueryable.GroupBy(empqueryable, "new (State, Department)", "it");
How can I get back the Key and corresponding list of grouped items i.e IEnumerable of {Key, List} from dynamiclinqquery ?
I solved the problem by defining a selector that projects the Key as well as Employees List.
var eq = empqueryable.GroupBy("new (State, Department)", "it").Select("new(it.Key as Key, it as Employees)");
var keyEmplist = (from dynamic dat in eq select dat).ToList();
foreach (var group in keyEmplist)
{
var key = group.Key;
var elist = group.Employees;
foreach (var emp in elist)
{
}
}
The GroupBy method should still return something that implements IEnumerable<IGrouping<TKey, TElement>>.
While you might not be able to actually cast it (I'm assuming it's dynamic), you can certainly still make calls on it, like so:
foreach (var group in dynamiclinqquery)
{
// Print out the key.
Console.WriteLine("Key: {0}", group.Key);
// Write the items.
foreach (var item in group)
{
Console.WriteLine("Item: {0}", item);
}
}

Categories

Resources