how to change the value of dictionary using linq based on key? - c#

I have a Dictionary which is of type,
Dictionary<string, string> newdictionary = new Dictionary<string, string>();
newdictionary.Add("12345", "chip1");
newdictionary.Add("23456", "chip2");
Now i have a List which is of type
internal class CustomSerial
{
public string SerialNo { get; set; }
public decimal ecoID { get; set; }
}
var customList = new List<CustomSerial>();
CustomSerial custObj1= new CustomSerial();
custObj1.ecoID =1;
custObj1.SerialNo = "12345";
customList.Add(custObj1);
CustomSerial custObj2 = new CustomSerial();
custObj2.ecoID = 2;
custObj2.SerialNo = "23456";
customList.Add(custObj2);
Now i need to update the Initial dictionary by Filtering the Keys with ther SerialNumber and Replacing the values with the ecoID.
When i try this, it gives
foreach (KeyValuePair<string, string> each in newdictionary)
{
each.Value = customList.Where(t => t.SerialNo == each.Key).Select(t => t.ecoID).ToString();
}
System.Collections.Generic.KeyValuePair.Value' cannot be assigned to -- it is read only

LIN(Q) is a tool to query something not to update it.
However, you can first query what you need to update. For example:
var toUpdate = customList
.Where(c => newdictionary.ContainsKey(c.SerialNo))
.Select(c => new KeyValuePair<string, string>(c.SerialNo, c.ecoID.ToString()));
foreach(var kv in toUpdate)
newdictionary[kv.Key] = kv.Value;
By the way, you get the "KeyValuePair.Value' cannot be assigned to it is read only" exception because aKeyValuePair<TKey, TValue> is a struct which cannot be modified.

You'd have the simplest in this form: though I don't see why you are assigning the same value but the method applies regardless
var dictionary = new Dictionary<string, string>() { { "12345", "chip1" }, { "23456", "chip2" } };
var customList = new List<CustomSerial>() { new CustomSerial() { ecoID = 1, SerialNo = "12345" }, new CustomSerial() { ecoID = 2, SerialNo = "23456" } };
dictionary.Keys.ToList().ForEach(key =>
{
dictionary[key] = customList.FirstOrDefault(c => c.SerialNo == key).SerialNo;
});

Related

looking for better way to fetch all Data Types and prepare a kind of dictionary with data type as key and a filter list as value

I have below class structure and a list of CollectionInstance
public class CollectionInstance
{
public string Name { get; set; }
public List<CollectionProperty> CollectionProperties { get; set; }
}
public class CollectionProperty
{
public string Name { get; set; }
public object Value { get; set; }
public string DataType { get; set; }
}
Here is list of CollectionInstance. Currently it has only two data types double and string, but I have more data types
var lstCollectionInstances = new List<CollectionInstance>
{
new CollectionInstance
{
Name = "A",
CollectionProperties = new List<CollectionProperty>
{
new CollectionProperty {Name = "P1", Value = 10, DataType = "Double"}
}
},
new CollectionInstance
{
Name = "A",
CollectionProperties = new List<CollectionProperty>
{
new CollectionProperty {Name = "P2", Value = "H1", DataType = "String"}
}
},
new CollectionInstance
{
Name = "B",
CollectionProperties = new List<CollectionProperty>
{
new CollectionProperty {Name = "P1", Value = 20, DataType = "Double"}
}
},
new CollectionInstance
{
Name = "B",
CollectionProperties = new List<CollectionProperty>
{
new CollectionProperty {Name = "P2", Value = "H2", DataType = "String"}
}
},
};
Now my goal to fetch all the different data type and filter list of CollectionInstance based on the data type. May be a dictionary or could be other collection as well, where I should store data type as key and filtered CollectionInstance as a value.
I tried below, but what could be the best way?
var dictionary = new Dictionary<string, List<CollectionInstance>>();
var dataTypesGroups = lstCollectionInstances
.SelectMany(x => x.CollectionProperties).GroupBy(x => x.DataType);
foreach (var dataType in dataTypesGroups)
{
dictionary.Add(dataType.Key, GetFilterData(lstCollectionInstances, dataType.Key));
}
private static List<CollectionInstance> GetFilterData(IEnumerable<CollectionInstance> lst, string dataType)
{
return lst.Where(x => x.CollectionProperties.Any(y => y.DataType == dataType)).ToList();
}
You could keep reference to parent CollectionInstance when grouping and reuse that when selecting results:
lstCollectionInstances
.SelectMany(x => x.CollectionProperties, (i, c) => new {CollectionInstance = i, CollectionProperty = c})
.GroupBy(x => x.CollectionProperty.DataType)
.ToDictionary(c => c.Key, c => c.Select(d => d.CollectionInstance) )
UPD
here we leverage this overload of .SelectMany(). So Instead of List<CollectionProperty> you end up having List<Tuple<CollectionInstance,CollectionProperty>> (well, i opted for anonymous type, but this does not matter much). You basically enhance each child object with reference to its parent. And since all these are just references - you don't trade a lot of memory for having it.
And when you group it - you get an option to not select the CollectionProperty, but rather the parent object directly.
I hope this makes sense
Build a dictionary that for each data type stores a list of instances with a property of the key data type.
var result = instances
.SelectMany(x => x.Properties)
.Select(x => x.DataType)
.Distict()
.ToDictionary(x => x, x => GetInstancesWithPropertyOfType(x, instances));
Given the following is defined:
public class Instance
{
public string Name { get; set; }
public List<Property> Properties { get; set; }
}
public class Property
{
public string Name { get; set; }
public object Value { get; set; }
public string DataType { get; set; }
}
List<Instance> GetInstancesWithPropertyOfType(string dataType, IEnumerable<Instance> instances) =>
instances.Where(x => x.Properties.Any(y => y.DataType == dataType)).ToList();
Personally think that using LINQ on this just makes it more unreadable and harder to understand. This is basically a two loop operation; for each x in instances/foreach y in x.properties/add x to dictionary indexed by y.z and would be most easily understood by keeping it as such. This minimizes the amount of work done by the framework too; here we create no unnecessary extra objects, lists, groupings etc in the quest to enumerate a 2-deep object hierarchy and create a dictionary, and even a coder who never saw LINQ can understand it:
var dictionary = new Dictionary<string, List<CollectionInstance>>();
foreach (var ci in lstCollectionInstances){
foreach(var cp in ci.CollectionProperties){
if(!dictionary.ContainsKey(cp.DataType))
dictionary[cp.Key] = new List<CollectionInstance>();
dictionary[cp.Key].Add(ci);
}
}
LINQ is a hammer; not every problem is a nail

create a dictionary of list<string> from an array of pocos?

Want to create a dictionary<string, list<string>>
from a result set that has a list of:
class catalog{
public string Name { get; set; }
public string CategoryName { get; set; }
public bool CategoryDeleted { get; set; }
}
I want the key to be the CategoryName and the value to be a List of Names.
Each Categories can have the same Name associated with it. One categoryname to many reportnames and it looks like this.
{Name = "rp1", CategoryName=cat1, CategoryDeleted = 0 }
{Name = "rp2", CategoryName=cat1, CategoryDeleted = 0 }
{Name = "rp3", CategoryName=cat1, CategoryDeleted = 0 }
{Name = "rp1", CategoryName=cat2, CategoryDeleted = 0 }
{Name = "rp2", CategoryName=cat2, CategoryDeleted = 0 }
I'd like a clean linq query that does this concisely.
what I have so far:
var dic = new Dictionary<string, List<string>>();
catalog.Select(x => x.CategoryName).Distinct().ToList().ForEach(x =>
{
dic.Add(x, catalog.Where(t => t.CategoryName == x).Select(t => t.ReportName).ToList());
});
I want to know if I can improve my query to not have to reference the catalog list twice. once to iterate and select my keys, then another to initialize the value when I make a dictionary add.
thanks!
Here is one more compact solution.
Dictionary<string, List<string>> dictionary = catalogs
.GroupBy(catalog => catalog.CategoryName, catalog => catalog.Name)
.ToDictionary(grouping => grouping.Key, grouping => grouping.ToList());
You need to use grouping:
var dict = items.GroupBy(c => c.CategoryName).ToDictionary(g => g.Key, g => g.Select(c => c.Name));

Select values from multiple dictionaries into list of objects with LINQ

I want to select OrderId & Row from multiple dictionaries and create a list of Orders which contains a list of Rows.
The code I'm struggling with this far is:
var listOfDictionaries = new List<Dictionary<string, string>>();
var dict1 = new Dictionary<string, string>();
dict1.Add("OrderId", "12345");
dict1.Add("Row", "1");
listOfDictionaries.Add(dict1);
var dict2 = new Dictionary<string, string>();
dict2.Add("OrderId", "97845");
dict2.Add("Row", "10");
listOfDictionaries.Add(dict2);
var dict3 = new Dictionary<string, string>();
dict3.Add("OrderId", "12345");
dict3.Add("Row", "2");
listOfDictionaries.Add(dict3);
var grouped = listOfDictionaries.SelectMany(d =>
d.Where(kvp =>
kvp.Key.ToLower().Contains("orderid") || kvp.Key.ToLower().Contains("row")))
.GroupBy(a => a.Key).ToList();
public class Order
{
public Order()
{
OrderRows = new List<OrderRow>();
}
public string OrderId { get; set; }
public List<OrderRow> OrderRows { get; set; }
}
public class OrderRow
{
public string OrderRowId { get; set; }
}
Anyone have a good idea how to solve this?
You could first select the orders and then the occording rows like so.
var orderFieldName = "OrderId";
var orders = listOfDictionaries.Select(dict => dict[orderFieldName]).Distinct();
var grouped = orders.Select(order => new Order
{
OrderId = order,
OrderRows = listOfDictionaries.Where(dict => dict[orderFieldName] == order)
.Select(item => new OrderRow {OrderRowId = item["Row"]}).ToList()
});
You need to do
var result = listOfDictionaries.Select(x => new { OrderId = x["OrderId"], Row = x["Row"] })
.GroupBy(x => x.OrderId)
.Select(x => new { OrderId = x.Key, Values = x.Select(y => y.Row) });
I feel you dictionary code will not run since it will not allow you to enter duplicate key value in same list. So here I have create a solution using list of Struct
class Stackoverflow
{
struct OrderData
{
public string OrderID { get; set; }
public string Row { get; set; }
}
static void Main(string[] args)
{
string strInputOrderID = "ORD1";
////Using Struct List
List<OrderData> objOrderDataList = new List<OrderData>();
OrderData objOrderData = new OrderData();
objOrderData.OrderID = "ORD1";
objOrderData.Row = "1";
objOrderDataList.Add(objOrderData);
objOrderData = new OrderData();
objOrderData.OrderID = "ORD2";
objOrderData.Row = "2";
objOrderDataList.Add(objOrderData);
objOrderData = new OrderData();
objOrderData.OrderID = "ORD3";
objOrderData.Row = "3";
objOrderDataList.Add(objOrderData);
objOrderData = new OrderData();
objOrderData.OrderID = "ORD1";
objOrderData.Row = "4";
objOrderDataList.Add(objOrderData);
List<OrderData> resultOrderDataList = (from data in objOrderDataList
where data.OrderID.Equals(strInputOrderID)
select data).ToList();
Console.ReadLine();
}
}
I have run it and its working fine.
Kindly check and do let me know in comment.
Just like #Nikhil Agrawal said:
Check this
var result = listOfDictionaries
.Select(x => new { OrderId = x["OrderId"], Row = x["Row"] })
.GroupBy(x => x.OrderId)
.Select(x => new Order{
OrderId = x.Key,
OrderRows = x.Select(y => new OrderRow{OrderRowId = y.Row}).ToList()
});

Getting a value from dictionary into a modelitem atribute

I am new in the dictionary datatype in C#. I got this model item:
public Dictionary<int, string> language { get; set; }
public string languageChoice { get; set; }
Then in my controller I got this:
[Route("{id}/settings")]
[HttpGet]
public async Task<HttpResponseMessage> GetProjectSettings(Guid id)
{
var projectSettings = new ProjectSettings
{
Id = id,
language = new Dictionary<int, string>() {
{1,"English"},
{2,"Spanish"}},
languageChoice = //get language by ID
};
if (projectSettings != null)
{
return Request.CreateResponse<ProjectSettings>(HttpStatusCode.OK, projectSettings);
}
else
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "Project not found");
}
}
I want to give the json object the integer and not the String of the language. How can I do that?
Kind regards
If you don't want to change your existing Dictionary<int, string> then you can just create a new Dictionary<int, int> from it.
var alteredDictionary = languages.ToDictionary(x => x.Key, x => x.Key);
Then do
var projectSettings = new ProjectSettings
{
Id = id,
language = new Dictionary<int, string>() {
{1,"English"},
{2,"Spanish"}},
languageChoice = alteredDictionary[1]// optional .ToString() if you want string
};
Where 1 is the language Id you want.

Convert Lambda into Linq Statement with nested for loop

Is there a way to rewrite the GetTransformedCollection method below so that it uses a Linq statement and not an expression? I'm currently trying to get around the “A lambda expression with a statement body cannot be converted to an expression tree” error.
public class Obj1
{
public int Id { get; set; }
public string[] Names { get; set; }
public string[] Tags { get; set; }
}
public class EntCollections
{
private List<Obj1> _results;
[SetUp]
public void SetUp()
{
_results = new List<Obj1>
{
new Obj1 {Id = 1, Names = new[] {"n1"}, Tags = new[] {"abc", "def"}},
new Obj1 {Id = 2, Names = new[] {"n2", "n3"}, Tags = new[] {"ghi"}},
new Obj1 {Id = 3, Names = new[] {"n1", "n3"}, Tags = new[] {"def", "xyz"}}
};
}
private static Dictionary<string, List<string>>
GetTransformedCollection(IEnumerable<Obj1> results)
{
var list = new Dictionary<string, List<string>>();
foreach (var result in results)
{
foreach (var id in result.Names)
{
if (list.ContainsKey(id))
{
list[id].AddRange(result.Tags);
}
else
{
list.Add(id, result.Tags.ToList());
}
}
}
return list;
}
[Test]
public void Test()
{
var list = GetTransformedCollection(_results);
Assert.That(list["n1"], Is.EquivalentTo(new [] { "abc", "def", "def", "xyz" }));
Assert.That(list["n2"], Is.EquivalentTo(new [] { "ghi" }));
Assert.That(list["n3"], Is.EquivalentTo(new [] { "ghi", "def", "xyz" }));
}
P.s I'm not too worried about the result type being a Dictionary, that was just the simplist way to express it as a return type.
I would personally use an ILookup, which is a good bet whenever you have a Dictionary<T1, List<T2>>, and is built with ToLookup():
// Flatten the objects (lazily) to create a sequence of valid name/tag pairs
var pairs = from result in results
from name in result.Names
from tag in result.Tags
select new { name, tag };
// Build a lookup from name to all tags with that name
var lookup = pairs.ToLookup(pair => pair.name, pair => pair.tag);
Idea is to find all keys for resulting dictionary and then find corresponding values from original sequence of Obj1
var distinctNames = results.SelectMany(val => val.Names).Distinct();
return distinctNames
.ToDictionary(name => name,
name => results
.Where(res => res.Names.Contains(name))
.SelectMany(res => res.Tags)
.ToList());

Categories

Resources