Generate a new list with key - c#

i would like to create a new list with key and values
List<object> r = new List<object>();
r.Add("apple");
r.Add("John");
return r;
when u Addwatch the r, you will see
[1] = apple
[2] = John
Questions: How do i make the [1] and [2] to be new key? When i addwatch the r, i would like to see [1] is replaced by Name. something as below:
Name = apple
TeacherName = John

Do you mean you want to use something like Dictionary<TKey, TValue>
example:
Dictionary<string, string> d = new Dictionary<string, string>();
d.Add("Name", "Apple");
d.Add("Teacher", "John");
or do you want an object to more strongly typed?
in this case you have to use your one class / struct
class MyObject
{
public string Name {get; set;}
public string Teacher {get; set;}
}
Then
var list = new List<MyObject>();
list.Add(new MyObject { Name = "Apple", Teacher = "John" });
list.Add(new MyObject { Name = "Banana", Teacher = "Setphan" });
then you can all it
var item = list[0];
var name = item.Name;
var teacher = item.Teacher;

It is completely incorrect to use a list for this kind of a data structure. You need to use use Dictionary , NameValueCollection or similar type.

You can transform your list:
List<object> r = new List<object>();
r.Add("apple");
r.Add("John");
r.Add("orange");
r.Add("Bob");
var dict = r.Where((o, i) => i % 2 == 0)
.Zip(r.Where((o, i) => i % 2 != 0), (a, b) => new { Name = a.ToString(), TeacherName = b.ToString() });
foreach (var item in dict)
{
Console.WriteLine(item);
}
Output:
{ Name = apple, TeacherName = John }
{ Name = orange, TeacherName = Bob }
And then transform to dictionary:
var result = dict.ToDictionary(d => d.Name, d => d.TeacherName);

You will need to use a Dictionary to do this. Not a List.
See http://msdn.microsoft.com/en-us/library/xfhwa508.aspx

I hope i dont make any syntax mistakes here...
Dictionary <string, int> r = new Dictionary<string,int>();
r.add("apple",1);
r.add("John",2)
console.WriteLine(r["apple"]);//returns value 1

Your question is not clear and hard to understand.
Do you mean to say you want keys instead of indexes ? Like Name instead of 1
Well then as Aliza and Bumble Bee have said you need to use a Dictionary instead of a List.
Here's a small example
IDictionary<string, Interval> store = new Dictionary<string, string>();
store.Add("Name","apple");
store.Add("TeacherName ", John);
foreach(KeyValuePair<string, string> e in store)
Console.WriteLine("{0} => {1}", e.Key, e.Value);

Related

How can I create a new List<T> based on two other List<T> and account for duplicates?

My first post. Humbled by this community. Thank you.
The goal: Create a new List<PropertyB> based on two other lists:
List<PropertyA> and another List<PropertyB>.
For each PropertyA in the List<PropertyA>, create a new PropertyB(), assigning the DisplayName to the new PropertyB's Name property. For each property in 'List', if the name from PropertyA matches PropertyB, assign the value to the new list's value property.
The problem: Accounting for Duplicate values. No data loss can occur between the lists.
The new list should include: Every PropertyA and every Value of the PropertyB list where there is a Name match.
The types:
My thoughts: My gut says the inner loop should check whether something has already been added to the collection. Or perhaps an accounting of duplicate values (ie: the index of duplicates?)
Any assistance is appreciated!
public class PropertyA{
private string DisplayName{get; set;}
private string Name {get; set;}
private string Value {get; set;}
}
public class PropertyB{
private string Name{get; set;}
private string Value{get; set;}
}
Initialization:
List<PropertyA> listA = new List<PropertyA>()
{
new PropertyA(){ DisplayName="LOB", Name="lineofbusiness", Value="test"},
new PropertyA(){ DisplayName="ABC", Name="alpha", Value="test2"},
new PropertyA(){ DisplayName="DEF", Name="beta", Value="test3"},
new PropertyA(){ DisplayName="GHI", Name="zeta", Value="test4"},
new PropertyA(){ DisplayName"Line of Business", Name="lineofbusiness", Value="test5"
};
List<PropertyB> listB = new List<PropertyB>()
{
new PropertyB(){ Name="lineofbusiness", Value="test789"},
new PropertyB(){ Name="alpha", Value="test234"},
new PropertyB(){ Name="lineofbusiness", Value="test456"},
new PropertyB(){ Name="beta", Value="test123"},
};
In Main:
List<PropertyB> newList = new List<PropertyB>();
foreach(PropertyA propA in listA){
PropertyB newProp = new PropertyB();
newProp.Name = propA.DisplayName;
foreach(PropertyB propB in listB){
if(propA.Name == propB.Name){
newProp.Value = propB.Value;
break;
}
}
newList.Add(newProp);
}
UPDATE:
The console output (if you choose) should be as follows:
LOB test789
ABC test234
DEF test123
GHI null
Line of Business test456
if you simply remove the break; you end up with:
LOB test456
ABC test234
DEF test123
GHI null
Line of Business test456
The inner loop will always assign the LAST name match value. That's a problem.
you can just fix your code, add a check for duplicates
List<PropertyB> newList = new List<PropertyB>();
foreach(PropertyA propA in listA)
{
PropertyB newProp = new PropertyB();
newProp.Name = propA.DisplayName;
foreach (var propB in listB)
{
if (propA.Name == propB.Name)
{
if( newList.Any(l =>l.Value==propB.Value )) continue;
newProp.Value = propB.Value;
break;
}
}
newList.Add(newProp);
}
but to make it more reliable I would offer this
List<PropertyA> newList = new List<PropertyA>();
foreach (var propA in listA)
{
var newProp = new PropertyA();
newProp.Name = propA.DisplayName;
newProp.DisplayName = propA.Name;
foreach (var propB in listB)
{
if (propA.Name == propB.Name)
{
if (newList.Any(l => l.Value == propB.Value
&& l.DisplayName==propA.Name)) continue;
newProp.Value = propB.Value;
break;
}
}
newList.Add(newProp);
}
var result = newList.Select(l => new PropertyB {Name=l.Name, Value=l.Value} );
both algorithms show the same result during the test
LOB test789
ABC test234
DEF test123
GHI null
Line of Business test456
I understood the process:
list of A needs turning into a list of B
Some of the list of B items might have a Value copied from some other list of B
var d = bList.ToDictionary(b => b.Name, b => b.Value);
var newB = aList.Select(a => new B { Name = a.DisplayName, Value = d.GetValueOrDefault(a.Name) } ).ToList();
You said no data shall be lost but I think inherently you must have to throw something away because B has fewer properties than A and some properties from B are used to "overwrite"/take the place of those in A..
I note also you have duplicated Name in your sample data list B, which the ToDictionary won't tolerate. You didn't specify how to resolve this but you'll have to choose (if it truly does occur) what value to pick or if to take multiple. This, for example, would tolerate duplicate names
var d = bList.ToLookup(b => b.Name, b => b.Value);
var newB = aList.Select(a => new B { Name = a.DisplayName, Value = d[a.Name]?.First() } ).ToList();
Again, this throws stuff away.. if you want to keep all the values you'll have to encode the Value somehow
Value = string.Join(",", d[a.Name])
for example
So, it looks like you want to keep all the duplicates and dispense them in order. We could do that by grouping these things into a list that we pull the items out of as we enumerate
var d = bList.GroupBy(b => b.Name, b => b.Value).ToDictionary(g => g.Key, g => g.ToList());
var newB = new List<B>();
foreach(var a in aList){
var b = new B { Name = a.DisplayName };
if(d.TryGetValue(a.Name, out var lst)){
b.Value = lst[0];
lst.RemoveAt(0);
}
}

List of objects with a list as a property of the object

Declaring a list of objects:
List<object> result = new List<object>();
and a list of int to store the ids:
List<int> ids = new List<int>();
I want to store in result objects containing the pair (string, list of int).
It works fine for the pair (string, int) but I want that when there are 2 identical strings to have only one object and the int values to be stored in a list.
ex: {pars = "xxx", id = 1} , {pars = "xxx", id = 2} becomes {pars = "xxx", id = (1,2 )}
For doing the initial functionality, I use a foreach through an object from which I take the string(pars) and the id:
foreach (dataBinding in myData)
{
var x = string.Join(" ", dataBinding.Params.Select(p => p.myDescription));
result.Add(new { pars = x, id = dataBinding.Id });
}
there could be more strings in Params, that's why I use the join.
As it is here it works by creating objects having the form (string, int). But my aim is to make it (string, list of int) and if there are two objects with same string to combine them as I wrote before.
I tried to add ids list as the second property of the object but probably I'm not doing it correctly.
result.Add(new { pars = x, ids = dataBinding.Id });
You can use LINQ, especially GroupBy:
Dictionary<string, List<int>> descriptionIDs = myData
.GroupBy(x => x.myDescription)
.ToDictionary(g => g.Key, g => g.Select(x => x.Id).ToList());
Now you have even a dictionary, not just a strange List<object> that contains anonymous types.
As someone mentioned, you can also use ToLookup which i'd also prefer:
var descriptionLookup = myData.ToLookup(x => x.myDescription);
Now you can get the ID-List easily:
var result = descriptionLookup.Select(g => new { pars = g.Key, ids = g.Select(x=> x.Id).ToList() }).ToList():
Perhaps I am not understanding the scenario fully but I suspect using the following would server your purpose.
List<Dictionary<string, List<int>>>
When the key doesn't exist you add it and when it does you just add to the List.
Below program depicts the current generic collection type, also allow to add a new value if Key Already exists.
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
MyProgram p = new MyProgram();
p.Add("First" , 5);
p.Add("Second" , 8);
p.Add("Third" , 9);
p.Add("First" , 6);
p.Add("First" , 7);
p.PrintDictionary();
}
}
public class MyProgram
{
private Dictionary<string, List<int>> dict = new Dictionary<string, List<int>>();
public void Add(string key, int value)
{
if (dict.ContainsKey(key))
{
dict[key].Add(value);
}
else
{
dict.Add(key, new List<int>() {value});
}
}
public void PrintDictionary()
{
foreach(var keyValue in dict)
{
Console.WriteLine("Key : " + keyValue.Key);
foreach(var val in keyValue.Value)
{
Console.WriteLine(string.Format("\t Value : {0}", val));
}
}
}
}
Output :
Key : First
Value : 5
Value : 6
Value : 7
Key : Second
Value : 8
Key : Third
Value : 9
Check this Live Fiddle.

LINQ: Convert Dictionary<int, List<MyObject>> to List<List<string>>

My Dictionary<int, List<MyObject>> result has..
Key Value
1 {"Chicago", 100}
1 {"Newyork", 200}
2 {"Minneapolis", 300}
want to convert it to List<List<string>> in below format.
{"Index", "City","Value"},
{1, "Chicago", 100},
{1, "Newyork", 200}
{2, "Minneapolis", 300}
This is what I have achieved so far
var list = result.Select(rec => new
{
Index = rec.Key,
City = rec.Value.Select(rec1 => rec1.City),
Value = rec.Value.Select(rec1 => rec1.Value)
}).ToList();
What I am getting is this..
{"Index", "City", "Value"},
{1, System.Linq.Enumerable.WhereSelectListIterator<MyObject, string>, System.Linq.Enumerable.WhereSelectListIterator<MyObject, int>},
{1, System.Linq.Enumerable.WhereSelectListIterator<MyObject, string>, System.Linq.Enumerable.WhereSelectListIterator<MyObject, int>},
{2, System.Linq.Enumerable.WhereSelectListIterator<MyObject, string>, System.Linq.Enumerable.WhereSelectListIterator<MyObject, int>}
May be I am missing Where condition. Please suggest.
public class MyObject
{
public MyObject(){}
public string City{get;set;}
public int Value{get;set;}
}
This is what you need:
var Result = result.SelectMany(r => r.Value.Select(x => new[] { r.Key.ToString(), x.City, x.Value.ToString() }.ToList()));
To prepend column names as the first element of the outer list:
Result.Insert(0, {"Index", "City","Value"}.ToList());
Do you need the output like this?
I have a solution for you. Try it.
Dictionary<int, List<MyObject>> result = new Dictionary<int, List<MyObject>>();
result.Add(1, new List<MyObject>() { new MyObject() { City = "Chicago", Value = 100 }, new MyObject() { City = "Newyork", Value = 200 } });
result.Add(2, new List<MyObject>() { new MyObject() { City = "Minneapolis", Value = 300 } });
var resultYouWant = result.SelectMany(p => p.Value.Select(a => new { Index = p.Key, a.City, a.Value })).ToList();
below code is work for you but not getting what is your usecase.
var list = result.Select(rec => new
{
Index = rec.Key,
City = rec.Value.City),
Value = rec.Value.Value)
}).ToList();
City = rec.Value.Select(rec1 => rec1.City),
That is creating an IEnumerable, not a string. Which is why you get System.Linq.Enumerable.WhereSelectListIterator out of it.
You may be better off using for loops here.
foreach(var kvp in result)
foreach(var value in kvp)
//Create string here and add it to your list.

Sum on a dynamic list that has ExpandoObject as items

I have a dynamic list with ExpandoObject as items :
List<dynamic> list = new List<dynamic>();
foreach (...)
{
var dynamicObject = new ExpandoObject() as IDictionary<string, Object>;
...
list.Add(dynamicObject);
}
How can I use .Sum() on a dynamic list ? I know the names of the properties
of that list , but Sum() does not take a string as argument .
Thanks
Assuming you have list like this:
var list = new List<dynamic>
{
new ExpandoObject(),
new ExpandoObject(),
new ExpandoObject(),
};
list[0].Foo = 1;
list[1].Foo = 2;
list[2].Foo = 3;
you can use ExpandoObject's properties as regular properties, if their names are known at compile-time:
var sum1 = list
.Sum(item => item.Foo);
or use dictionary syntax, if property names are known at run-time only:
var sum2 = list
.Sum(item => (int)((IDictionary<string, object>)item)["Foo"]);
You can use Sum(Func<TSource, int> selector)
For you example :
var values = new int[] {1, 2, 3, 4, 5};
List<dynamic> list = new List<dynamic>();
foreach (var value in values)
{
var dynamicObject = new ExpandoObject() as IDictionary<string, Object>;
dynamicObject[value.ToString()] = value;
list.Add(dynamicObject);
}
// Kind of ugly as cast but otherwise it has trouble to find the Count property
var result = list.Sum(x => (x as IDictionary<string, Object>).Count);
Console.WriteLine("Result: {0}", result);
Console.ReadLine();
To my mind you could try this :
var listSum = list.Sum(item => item.GetType().GetProperty("propertyName").GetValue());
I called list the list on which you want to call the Sum method.
Let me know if the result is what you want.

Lambda expression to loop through two concurrent dictionaries

I am trying to loop through two concurrent dictionaries like the code below, however I want to use a lambda expression instead
foreach (var s in sb_eventdata)
{
foreach (var f in final_data)
{
if (s.Value.Car.Equals(f.Value.Car))
{
Console.Writeline("Found!");
}
}
}
var values = sb_eventdata.Where(k => k.Value.Hometeam.Contains( ???? );
I'm really not sure what to pass into contains, I assume another lambda expression but what?
The closest linq expression to your loops would be:
var sb_eventdata = new Dictionary<string, string>{ {"a", "a"}, {"b", "b"}};
var final_data = new Dictionary<string, string>{{"a", "a"}, {"b", "b"}, {"c","c"}};
var result =
// first loop
sb_eventdata.Select(s =>
// second loop
final_data.Where(f => s.Value.Equals(f.Value)))
// flatten results (returns results from the first dictionary)
.SelectMany(x => x);
You can use a linq Intersect function to find like items in a list.
Then display all like items.
var foo = sb_eventdata.Select(o => o.Value.Car).Intersect(final_data.Select(o => o.Value.Car));
foreach (var item in foo)
{
Console.Writeline("Found!");
}
I think your friend is the Join() method.
In "LinqPad style":
void Main()
{
var a = new[] {
new Car("Opel",200),
new Car("Volkswagen",300),
new Car("Audi", 500)
};
var b = new[] {
new Car("Peugeot", 180),
new Car("Seat", 300),
new Car("Volvo", 480)
};
var c = a.Join(b, ak => ak.Value, bk => bk.Value, (ak,bk) => new {A=ak.Name,B=bk.Name,ak.Value});
c.Dump();
}
// Define other methods and classes here
class Car {
public string Name;
public int Value;
public Car (string name, int value) {
Name = name;
Value = value;
}
}
If you just want to know if both dictionary share at least one value, you can use Any:
if(sb_eventdata.Any(s =>
final_data.Any(f => s.Value.Car.Equals(f.Value.Car))))
Console.WriteLine("Found!");
or with Contains:
if(sb_eventdata.Any(s => final_data.ContainsValue(s.Value)))
Console.WriteLine("Found!");
and if you want to count how many of sb_eventdata are in final_data:
sb_eventdata.Where(s => final_data.ContainsValue(s.Value)).Count();

Categories

Resources