I have two List<T> objects:
For example:
List 1:
ID, Value where Id is populated and value is blank and it contains say IDs from 1 to 10.
1,""
2,""
...
10,""
List 2:
ID, Value and other attributes all filled with values but this list is a subset of List 1 in terms of IDs. (e.g only 3 items)
2,67
4,90
5,98
What I want is a merged list 1, but with updated values. Does anyone have any good extension method which will do this or any elegent code to perform this operation. The final list should be:
ID, Value
1,""
2,67 //value from list 2
3,""
4,90
5,98
6,""
...
10,""
use linq: list1=list2.Union(list1);
I would probably use a dictionary rather than a list:
// sample data
var original = new Dictionary<int, int?>();
for (int i = 1; i <= 10; i++)
{
original.Add(i, null);
}
var updated = new Dictionary<int, int>();
updated.Add(2, 67);
updated.Add(4, 90);
updated.Add(5, 98);
updated.Add(11, 20); // add
// merge
foreach (var pair in updated)
{
original[pair.Key] = pair.Value;
}
// show results
foreach (var pair in original.OrderBy(x => x.Key))
{
Console.WriteLine(pair.Key + ": " + pair.Value);
}
If you are talking about properties of an object, it will be trickier, but still doable.
This is O(m*n) but should do the job for arbitrary lists
foreach (var record in List1)
{
var other = List2.FirstOrDefault(x => x.Key == record.Key);
if(other != null) record.Value = other.Value;
}
If the lists are guaranteed ordered, then it could be brought down to O(n) at the cost of more code. The algortihm would be
Current items start as head of each list
While items remain in both lists
If the current item of list1 has lower key than list2 advance to next in list1
else if the current item of list2 has lower key than list1 advance to next in list2
else copy value from current list2 item into list1 item and advance both lists.
If you have both lists sorted by ID, you can use a variation of the classical merge algorithm:
int pos = 0;
foreach (var e in list2) {
pos = list1.FindIndex(pos, x => x.Id==e.Id);
list1[pos].Value = e.Value;
}
Note that this also requires list2 to be a strict subset of list1 in terms of ID (i.e. list1 really contains all ids of list2)
Of course you can also wrap this in an extension method
public static void UpdateWith<T>(this List<T> list1, List<T> list2)
where T:SomeIdValueSupertype {
int pos = 0;
foreach (var e in list2) {
pos = list1.FindIndex(pos, x => x.Id==e.Id);
list1[pos].Value = e.Value;
}
}
private void btnSearch_Click(object sender, EventArgs e)
{
String searchBy = cmbSearchBy.Text.ToString();
String searchFor = txtSearchFor.Text.Trim();
var List3 = (from row in JobTitleDB.jobList
where (row.JID.ToString()+row.JobTitleName.ToString().ToLower()).Contains(searchFor.ToLower())
select row).ToList();
if (searchBy == "All")
{
dgJobTitles.DataSource = null;
//dgJobTitles.DataSource = List1;
//dgJobTitles.DataSource = List2;
//dgJobTitles.DataSource = List1.Concat(List2);
//dgJobTitles.DataSource = List1.Union(List2);
dgJobTitles.DataSource = List3;
//dgJobTitles.DataSource=List1.AddRange(List2);
}
}
Dictionary<int, string> List1 = new Dictionary<int, string>();
List1.Add(1,"");
List1.Add(2,"");
List1.Add(3,"");
List1.Add(4,"");
List1.Add(5,"");
List1.Add(6,"");
Dictionary<int, string> List2 = new Dictionary<int, string>();
List2.Add(2, "two");
List2.Add(4, "four");
List2.Add(6, "six");
var Result = List1.Select(x => new KeyValuePair<int, string>(x.Key, List2.ContainsKey(x.Key) ? List2[x.Key] : x.Value)).ToList();
Related
I have a List<List<string>> with three nested lists. Now I need to check if List[1] equals a certain string and if so, check if the value at this index in List[2] has another certain string. If both conditions return true, then I need to get that certain index and get the item of List[0].
For example:
var list = Titles[0];
var list2 = Titles[1];
var list3 = Titles[2];
foreach (var item in list2)
{
if (item.Contains("Dt."))
{
int idx = list2.IndexOf(item);
var value = list3.ElementAt(idx);
if (value.Contains("25.04.2017"))
{
var newList = list.ElementAt(idx);
}
}
}
This approach doesn't seem very efficient in regards to performance, especially if the nested list contains ~9000 items.
I tried to get the result via lambda expressions first, but I'm not sure if this is the right approach either.
What would be the best or most efficient solution?
Eliminate ElementAt with direct access to index. I believe ElementAt iterates over List in order to get i'th element
Eliminate usage of IndexOf with index provided by for loop I believe IndexOf iterates over List in order to find matching element.
var list = Titles[0];
var list2 = Titles[1];
var list3 = Titles[2];
for (int i = 0 ; i < list2.Count; ++ i)
{
var item = list2[i];
if (item.Contains("Dt."))
{
var value = list3[i];
if (value.Contains("25.04.2017"))
{
var newList = list[i];
}
}
}
Note if size of list2 is greater than size of list or list3 then you potentially get IndexOutOfRangeException
Lambda equivalent for your code:
if(list2.Any(item => item.Contains("Dt.")))
{
int idx = list2.IndexOf("Dt.");
if(list3.ElementAt(idx).Contains("25.04.2017"))
{
var newList = list.ElementAt(idx);
}
}
for (int i = 0; i < list2.Count; ++i)
{
var item = list2[i];
if (item.Contains("Dt."))
{
var value = list3[i];
if (value.Contains("25.04.2017"))
{
var newList = list[i];
break; // Break the loop :-)
}
}
}
I have 2 list which names are listA and listB.
I want to remove strings in listB which are in listA, but I want to do this in this way:
if listA contains: "bar", "bar", "bar", "foo"
and listB contains : "bar"
it removes only 1 bar and the result will be:
"bar", "bar", "foo"
the code I wrote removes all "bar":
List<string> result = listA.Except(listB).ToList();
You can try to remove it one by one:
foreach (var word in listB)
listA.Remove(word);
The Remove method will only remove one element at a time and is not throwing exception (but returning false) when the item is not found: https://msdn.microsoft.com/en-us/library/cd666k3e(v=vs.110).aspx
var listA = new List<string>() { "bar", "bar", "bar", "foo" };
var listB = new List<string>() { "bar" };
foreach (var word in listB){
listA.Remove(word);
}
Here is a more efficient way to do that:
var countB = new Dictionary<string, int>(listB.Count);
foreach (var x in listB)
{
int count;
countB.TryGetValue(x, out count);
countB[x] = count + 1;
}
listA.RemoveAll(x =>
{
int count;
if (!countB.TryGetValue(x, out count)) return false;
if (count == 1)
countB.Remove(x);
else
countB[x] = count - 1;
return true;
});
This is a faster method but it is likely to change the order of elements of first list. Steps:
Map the listA to a Dictionary<string, int> (let's call it listAMap), where key is the element of the list and value is the total number of times that value has occurred in listA;
Iterate through listB and for every element of listB, if that element is in the listAMap, reduce its count;
Get the keys of listMapA using Keys property of C# dictionaries, and iterate through all the keys. For every key which has positive value, add that key to another list a total of its count times. So if an entry is "bar" -> 2, then add "bar" twice in the new list.
Total run time of the algorithm is O(m + n), where m and n are number of elements in both the original lists. It is a better running time than other approaches mentioned here which have O(m * n) running time. Obviously this algorithm uses more space.
Supportive Code for the algorithm above:
//Step-1: Create the dictionary...
var listAMap = new Dictionary<string, int>();
foreach (var listAElement in listA)
{
listAMap.ContainsKey(listAElement) ? listAMap[listAElement]++ : listAMap.Add(listAElement, 1);
}
// Step-2: Remove the listB elements from dictionary...
foreach (var listBElement in listB)
{
if (listAMap.Contains(listBElement)) listAMap[listBElement]--;
}
//Step-3: Create the new list from pruned dictionary...
var prunedListA = new List<string>();
foreach (var key in listAMap.Keys)
{
if (listAMap[key] <= 0) continue;
for (var count = 0; count < listAMap[key]; count++)
{
prunedListA.Add(key);
}
}
//prunedListA contains the desired elements now.
Dictionary<String, UInt32> dict = new Dictionary<String, UInt32>();
dict.Add("1", 1);
dict.Add("4", 4);
dict.Add("5", 5);
Quick way to convert all keys to array is
string[] keys = dict.Keys.ToArray();
This is how memory representation of array would look
keys[0] = "1" keys[1] = "4" keys[2] = "5"
What I want is, key string value should be in array at the index specified by value.
keys[1] = "1" keys[4] = "4" keys[5] = "5"
This is what I tried, it works..
Int32 count = -1;
foreach (KeyValuePair<String, UInt32> kvp in dict)
{
if (kvp.Value > count)
{
count = (Int32)kvp.Value;
}
}
String[] labelarray = new String[count + 1];
foreach (KeyValuePair<String, UInt32> kvp in dict)
{
labelarray[kvp.Value] = kvp.Key;
}
But Is there any better, cleaner way to do above?
You can use Enumerable.ToDictionary to revert keys and values.
var revertedDictionary = list.ToDictionary(x => x.Value, x => x.Key);
Another way with List<T> or Array has 2 parts: searching max index and filling collection:
if(!list.Any())
{
// use different behavior if you need
return new string[0];
}
var maxValue = list.Values.Max();
var newList = new string[maxValue];
Enumerable
.Range(0, (int)maxValue)
.ToList()
.ForEach(x => newList[x] = list.ContainsValue((uint)x) ? x.ToString() : string.Empty);
// improve memory usage by preventing to create new List in ToList() method
foreach(var index in Enumerable.Range(0, (int)maxValue))
{
newList[index] = list.ContainsValue((uint)index) ? index.ToString() : string.Empty;
}
I have this piece of code, which seems to support passing into it many list arguments and it would compare each one against each other one to find a common list among all of the other lists simultaneously.
I cannot figure out how to pass multiple Lists into a single argument thats a IEnmerable.
Say my test code looks like this
List<uint> List1 = new List<uint>();
List<uint> List2 = new List<uint>();
List<uint> List3 = new List<uint>();
List<uint> Commons = FindCommon(List1, List2, List3); //no compile
List<uint> Commons = FindCommon<List<uint>>(List1, List2, List3); //no compile?? why
How do I call this properly?? must I merge them somehow into a IEnumerable?? or must I somehow combine them all into 1 list yet keeping some kind of invisible divider?
static List<T> FindCommon<T>(IEnumerable<List<T>> lists)
{
Dictionary<T, int> map = new Dictionary<T, int>();
int listCount = 0; // number of lists
foreach (IEnumerable<T> list in lists)
{
listCount++;
foreach (T item in list)
{
// Item encountered, increment count
int currCount;
if (!map.TryGetValue(item, out currCount))
currCount = 0;
currCount++;
map[item] = currCount;
}
}
List<T> result= new List<T>();
foreach (KeyValuePair<T,int> kvp in map)
{
// Items whose occurrence count is equal to the number of lists are common to all the lists
if (kvp.Value == listCount)
result.Add(kvp.Key);
}
return result;
}
P.S.> FindCommon is broken somehow it doesn't work properly, probably isn't intended what I thought it should do.. it doesn't check all lists simultaneously together only linear one list at a time with another list which breaks it's purpose, it counts them up.. but it doesn't keep track from which list they came from.
Fixed it like so, this method worked as intended.
public static List<T> FindCommon<T>(params List<T>[] lists)
{
SortedDictionary<T, bool>
current_common = new SortedDictionary<T, bool>(),
common = new SortedDictionary<T, bool>();
foreach (List<T> list in lists)
{
if (current_common.Count == 0)
{
foreach (T item in list)
{
common[item] = true;
}
}
else
{
foreach (T item in list)
{
if (current_common.ContainsKey(item))
{
common[item] = true;
}
}
}
if (common.Count == 0)
{
current_common.Clear();
break;
}
SortedDictionary<T, bool>
swap = current_common;
current_common = common;
common = swap;
common.Clear();
}
return new List<T>(current_common.Keys);
}
You can accomplish this nicely using the params keyword. In your example:
static List<T> FindCommon<T>(params List<T>[] lists)
That would achieve usage:
List<uint> Commons = FindCommon(List1, List2, List3);
You need to Add list1,list2,list3 in another list like ,
List<List<uint>> commonLists=new List<List<uint>>();
commonLists.Add(list1);
commonLists.Add(list2);
commonLists.Add(list3);
List<uint> commons=FindCommon<List<unit>>(commonLists);
I have a list of list of items like in the following.
List<List<int>> testItems=new List<List<int>>();
I have added items to it like this.
List<int> item=new List<item>();
item.clear();
item.Add(3);
item.Add(5);
item.Add(7);
testItems.Add(item);
item.clear();
item.Add(10);
item.Add(3);
testItems.Add(item);
item.clear();
item.Add(1);
item.Add(8);
item.Add(3);
item.Add(9);
testItems.Add(item);
Now what I want to do is I want to extract the elements common to all the lists and add it to another list called finalList.
List<int> finalList = new List<int>();
I have tried the following.
int count;
foreach (List<int> a in testItems)
{
count = 0;
foreach (int b in a)
{
foreach (List<int> c in testItems)
{
foreach (int d in c)
{
if (d == b)
{
count++;
break;
}
}
}
if (count == a.Count)
finalList.Add(b);
}
}
I know this solution is not optimal and does not provide what I need. It can possibly add the same item several times. Can someone help me to fix it ?
Your code for adding items is not adding three different items. As you are reusing the item, the code is equivalent to:
List<int> item=new List<int>();
item.Add(1);
item.Add(8);
item.Add(3);
item.Add(9);
testItems.Add(item);
testItems.Add(item);
testItems.Add(item);
You need to create new items instead of reusing the same item:
List<int> item = new List<int>();
item.Add(3);
item.Add(5);
item.Add(7);
testItems.Add(item);
item = new List<int>();
item.Add(10);
item.Add(3);
testItems.Add(item);
item = new List<int>();
item.Add(1);
item.Add(8);
item.Add(3);
item.Add(9);
testItems.Add(item);
For a more efficient way to get the numbers common to all items, you can loop through all values in the first item, and check which exists in all the other items:
List<int> finalList =
testItems[0].Where(
i => testItems.Skip(1).All(x => x.Contains(i))
).ToList();
You can use LINQ method Intersect
You will get something like this:
IEnumerable<int> result = testItems[0];
for(int i = 1; i < testItems.Count; i++)
{
result = result.Intersect(testItems[i]);
}
You get your intersection in the result variable.
But are you really sure that you need this strange looking list of lists?