I was wondering if I could replace this foreach with LINQ queries somehow (if possible):
Online playpen: https://ideone.com/PQEytf
using System;
using System.Collections.Generic;
using System.Linq;
public class Test
{
public static void Main()
{
// dummy inputs for test sake
var keys = new[] { "key1", "key2" };
var services = new Dictionary<string, Service>
{
{"key1", new Service {Components = new Dictionary<string, string> {{"comp1", "value1"}}}},
{"key2", new Service {Components = new Dictionary<string, string> {{"comp2", "value2"}}}}
};
var serviceComponents = GetServiceComponents(keys, services);
// do something with it
}
public static IEnumerable<ServiceComponent> GetServiceComponents(string[] keys, Dictionary<string, Service> services)
{
var serviceComponents = new List<ServiceComponent>();
// this is the foreach that I want to lose
foreach (
var key in
keys.Select(
name =>
services.FirstOrDefault(
x => x.Key.Equals(name))))
{
serviceComponents.AddRange(key.Value.Components.Select(component => new ServiceComponent
{
Name = key.Key,
Service = component.Key,
Value = component.Value,
}));
}
return serviceComponents.ToArray();
}
}
public class Service
{
public Dictionary<string, string> Components { get; set; }
}
public class ServiceComponent
{
public string Name { get; set; }
public string Service { get; set; }
public string Value { get; set; }
}
Yes, what you're looking for is SelectMany. That allows you to turn each item in the sequence into another sequence, and then flatten all of those sequences into a single sequence. (You're accomplishing the same thing, without the deferred execution, by putting all of the sequences into a list.)
return keys.SelectMany(name => services.FirstOrDefault(x => x.Key.Equals(name))
.Value.Components
.Select(component => new ServiceComponent
{
Name = name.Key,
Service = component.Key,
Value = component.Value,
}))
.ToArray();
Having said that, what this query is doing is taking each of your keys, finding the corresponding item in services using a linear search, and then mapping the result. Rather than doing a linear search using FirstOrDefault, you can use the dictionary's native ability to effectively and efficiently find values for each key:
return keys.SelectMany(key => services[key].Components
.Select(component => new ServiceComponent
{
Name = key,
Service = component.Key,
Value = component.Value,
}))
.ToArray();
To extend #Servy's example, I often find the LINQ expression syntax to be easier to read than the lambda SelectMany (it translates to the same thing). Here is his query using query expressions:
return from key in keys
from component in services[key].Components
select new ServiceComponent
{
Name = key,
Service = component.Key,
Value = component.Value,
}))
.ToArray();
What you need is SelectMany, however, by using FirstOrDefault you're opening yourself up to a NullReferenceException (as the default value for any reference type is null). If you intend to have a NullReferenceException thrown when an element of keys is not a key in services then you can use other answers which leverage SelectMany. However, is you do not intend a NullReferenceException, then you should use something like the following:
return services.Where(pair => keys.Contains(pair.Key))
.SelectMany(pair =>
pair.Value.Components
.Select(component => new ServiceComponent
{
Name = pair.Key,
Service = component.Key,
Value = component.Value
}))
.ToArray();
This statement removes all key-value pairs from services whose key is not in the keys array, then turns each element of each pair's Components into a new ServiceComponent object, with the SelectMany making a single flat list out of the new ServiceComponents.
Related
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp5
{
class Validator
{
static void Main()
{
var metaValues = new List<Meta>
{
new Meta(4, 15, true),
new Meta(5, 20, false)
};
var requestDict = new Dictionary<int, List<int>>
{
{4, new List<int>{15,20} },// error not exist
{5, new List<int>{25} }, // error its false
{6, new List<int>{30} } // error not exist
};
var matchedIds = new List<int>();
if (metaValues.Any())
{
foreach (var ob in metaValues)
{
if (requestDict.ContainsKey(ob.Id))
{
matchedIds.Add(ob.Id);
var valuesDict = requestDict[ob.Id];
//here i cant get all the values and its Active of meta.Id
}
}
}
foreach (var key in requestDict.Keys)
{
if (!matchedIds.Contains(key))
Console.WriteLine("Invalid");
}
}
}
public class Meta
{
public int Id { get; private set; }
public int Value { get; private set; }
public bool IsActive { get; private set; }
public Meta(int id, int value, bool isActive)
{
Id = id;
Value = value;
IsActive = isActive;
}
}
}
iterating dictionary with object causing performance issue since everytime dictionary key has to be iterated in an list of object so i am trying to take object and lookup in dictionary on below condition
Invalid when meta.Id does not exist in dictionary key
Invalid when one of the meta.Value does not exist in dictionary values List
Inactive when meta.Id and meta.value match with dictionary but meta.isactive is false
I probably shouldn't bother answering since:
The code is quite messy
It does not compile
The question is very unclear
However, for some reason I feel like I understand a little what you're trying to do and wanted to provide some help.
First, let's NOT name a class with the same name as a built-in type (System.Object). Perhaps Item is generic enough? Also, you appear to instantiate instances of this class by calling a constructor that doesn't exist, so let's add that constructor as well:
public class Item
{
public int Id { get; }
public int Value { get; }
public bool IsActive { get; }
public Item(int id, int value, bool isActive)
{
Id = id;
Value = value;
IsActive = isActive;
}
}
Now we can create our list of Item objects by calling the constructor:
var items = new List<Item>
{
new Item(4, 15, true),
new Item(5, 20, false)
};
It also appears that you're creating a dictionary that contains a Key of type int that maps to Item.Id, and a Value of type List<int> that sort-of maps to Item.Value (though Item.Value is a single int). A problem in the code you posted is that you're trying to add two items with the same Key value of 4, which is not legal for a Dictionary - all the keys must be unique. To fix this, I'm using unique keys:
var requests = new Dictionary<int, List<int>>
{
{4, new List<int> {15}},
{5, new List<int> {20}},
{6, new List<int> {25}},
{7, new List<int> {30}}
};
Next it appears that you're trying to create a List<int> of numbers representing the Item.Id values that exist as dictionary keys. This can be done with a System.Linq extension method:
var matchedIds = items
.Where(item => requests.ContainsKey(item.Id))
.ToList();
And finally, it's not exactly clear what you want to do with this list, but it appears you want to do something if either an Item.Id does not exist in the dictionary, or the Item.Id exists but the Item.Value is not in the list, or the item does exist, but the Item.IsActive value is false, or some other combination of these properties.
Here's how to get those items:
var matchedIds = items
.Where(item => requests.ContainsKey(item.Id))
.ToList();
var matchedIdsAndValues = matchedIds
.Where(item => requests[item.Id].Contains(item.Value))
.ToList();
var matchedIdsMissingValue = matchedIds
.Where(item => !requests[item.Id].Contains(item.Value))
.ToList();
var unmatchedIds = items
.Where(item => !requests.ContainsKey(item.Id))
.ToList();
var matchedIdAndValueButNotActive = matchedIdsAndValues
.Where(item => !item.IsActive)
.ToList();
Hope this helps!
I have table called Asset. It has lot of columns. I only want to select two of them and use them separately.
Both of these columns are strings.
Linq query :
public static List<string> GetAssetIdsWithNames()
{
using (var db = DbManager.Get())
{
var result = db.Assets.SelectMany(i=> new[] { i.AssetName, i.AssetId }).Distinct().ToList();
return result;
}
}
Where I want to use them :
var assetList = AssetManager.GetAssetIdsWithNames();
//CURRENCYBOX IS A DROPDOWN
CurrencyBox.DataSource = assetList;
CurrencyBox.DataBind();
foreach (var item in assetList)
{
CurrencyBox.DataValueField = //asset id goes here
CurrencyBox.DataTextField =//asset name goes here
break;
}
You cannot access the anonymous type outside of the local scope.
Anonymous types can only be returned as Object outside their local scope and their properties inspected via reflection.
So in this scenario, you are likely better off to use a typed data contract and map from your Asset entity instead and then access it from your calling method.
Your use of SelectMany seems odd too, you probably are after Select instead.
public class AssetDto
{
public string Name { get;set; }
public string Id { get; set; }
}
public static List<AssetDto> GetAssetIdsWithNames()
{
using (var db = DbManager.Get())
{
var result = db.Assets.Select(i=> new AssetDto { Name = i.AssetName, Id = i.AssetId }).ToList();
return result;
}
}
You could use named value tuples for that so you don't need to create an extra class
public static List<(string Name, int Id)> GetAssetWithIds()
{
using (var db = DbManager.Get())
{
var result = db.Assets
.Select(a => new { a.AssetName, a.AssetId })
.Distinct().AsEnumerable()
.Select(a => (a.AssetName, a.AssetId))
.ToList();
return result;
}
}
You will need to add System.ValueTuple
I want to replace words in a string that matches a keywords stored in an array with data from matching column in a table.
My model is People
public class People()
{
public int Id { get; set; }
public string Title { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
The method i have tried is :
public void ProcessString(string message)
{
using (DBEntities db = new DBEntities())
{
var people = db.People.ToList();
foreach(var person in people)
{
string[] keyword = {"#Title", "#Name", "#Surname"};
for (int i=0; i<keyword.Length; i++)
{
string updatedString = Regex.Replace(body, keyword[i], matchingcolumndata);
}
}
}
So instead of matchingcolumndata in the string updatedString = Regex.Replace(body, keyword[i], matchingcolumndata); line i want to put the data that is matching the column in my table People. Please help with the question if its not straight to the point.
From my understand it may be you are looking for
foreach(var person in people)
{
string[] keyword = {"#Title", "#Name", "#Surname"};
body= body.Replace(keyword[0],person.Title);
body= body.Replace(keyword[1],person.Name);
body= body.Replace(keyword[2],person.Surname);
}
It doesn't make any sense to declare your keywords array inside the loop. If you want to do this in a neat way (and subject to modifications easily), you can create a map between the keyword and the property of the Person. Like this:
using (DBEntities db = new DBEntities())
{
var people = db.People.ToList();
// maps each keyword to a property in the Person class
var keywordPropertyMapping = new Dictionary<string, Func<Person, string>>()
{
{ "#Title", p => p.Title },
{ "#Name", p => p.Name },
{ "#Surname", p => p.Surname }
};
foreach (var person in people)
{
foreach(var keywordFunc in keywordPropertyMapping)
{
body = body.Replace(keywordFunc.Key, keywordFunc.Value(people));
}
}
}
For any new keyword, you just add one simple line inside the dictioanry keywordPropertyMapping, and the magic works.
If you're not familiar with Func<T, TResult> which is one critical part of the Linq magic, then read the Docs:
Encapsulates a method that has one parameter and returns a value of
the type specified by the TResult parameter.
You can use this delegate to represent a method that can be passed as
a parameter without explicitly declaring a custom delegate. The
encapsulated method must correspond to the method signature that is
defined by this delegate. This means that the encapsulated method must
have one parameter that is passed to it by value, and that it must
return a value.
I have this class:
public class Document
{
public string ID { get; set; }
public string Type { get; set; }
public bool Checked {get;set; }
}
I create a set of 10 elements using Enumerable.Repeat static method:
var list = Enumerable.Repeat<Document>(
new Document
{
ID="1",
Type ="someType"
Checked = true
}, 10).ToList<Document>();
These creates 10 Documents all with the same properties. I need that some of them, for instance, the first 5 elements of the list list have the Checked property to false.
How can I achieve it, using as possible linq?
Note that your original sample has a bug because it's creating a 10 element List<Document> that only has 1 actual Document object. Here is a better way of doing it
Enumerable
.Range(1, 10)
.Select(i =>
new Document() {
ID = "1",
Type = "someType",
Checked = i <= 5
})
.ToList();
EDIT
Changed the code to be simpler. My original response was to editing an already existing list for which the following can be done
list.Take(5).ForEach(x => { x.Checked = false });
Note that you may have to define a simple ForEach method for this operation. If you don't have one defined here is an example
static class Extensions {
internal static void ForEach<T>(this IEnumerable<T> e, Action<T> action) {
foreach (var item in e) {
action(item);
}
}
}
Alternate idea to accomplish what you're asking for (also populates your ID column with something other than "1"):
var list = Enumerable.Range(1, 10)
.Select(i => new Document
{
ID = i.ToString(),
Type = "someType",
Checked = (i > 5)
}).ToList();
If I have this ConcurrentDictionary:
public class User
{
public string Context { get; set; }
public bool Owner { get; set; }
}
protected static ConcurrentDictionary<User, string> OnlineUsers = new ConcurrentDictionary<User, string>();
Does anyone know how I would get the value of Owner if I already have the value of the Context? Basically I want to do a "find" using the Context. Thanks.
Does anything stop you from using standard Linq FirstOrDefault() method like so:
var item = OnlineUsers.FirstOrDefault(kvp => kvp.Key.Context == myContext);
How obout something like
var ou = OnlineUsers.First(x => x.Key.Context == "TADA");
It sounds like you need a Dictionary<string, User> which, when given the context as a string will tell you which User it corresponds to. If you are going to need to perform this lookup several times, and there isn't a problem using the additional memory, it may be worth creating such a dictionary.
If you are only going to be doing the search once or twice, or the mappings will be changing so often that you can't keep the dictionary up to date, then you can simply do a linear search on the dictionary using a foreach loop, or using a LINQ method such as First or Where, as demonstrated in other answers.
Here you go:
ConcurrentDictionary<User, string> dict = new ConcurrentDictionary<User, string>();
dict.TryAdd(new User() { Context = "a", Ownder = false }, "aa");
dict.TryAdd(new User() { Context = "b", Ownder = false }, "bb");
dict.TryAdd(new User() { Context = "c", Ownder = true }, "cc");
dict.TryAdd(new User() { Context = "d", Ownder = false }, "dd");
IEnumerable<User> list = dict.Keys.Where(p => p.Context == "a");