I have a list of these objects that I would like to convert to a dictionary:
public class Thing
{
public Guid ParentKey { get; set; }
public int Position { get; set; }
public bool Unconfigured { get; set; }
public void Replace(Thing thing);
}
I would like the key and value to be Thing objects where the ParentKey and Position are the same. The key object must have Unconfigured set to true and the value would be a sibling object that has the same ParentKey and Position but a false Unconfigured state. I'm trying to create a pairs of objects that have the same ParentKey, Position but have different Unconfigured states.
Example:
var things = new List<Thing>();
things.Add(new Thing { ParentKey = new Guid("8a1211d2-f42b-4dd2-b6a3-7f4ab4a44a8d"), Position = 1, Unconfigured = true });
things.Add(new Thing { ParentKey = new Guid("8a1211d2-f42b-4dd2-b6a3-7f4ab4a44a8d"), Position = 1, Unconfigured = false });
things.Add(new Thing { ParentKey = new Guid("35f22dba-7789-49f4-8982-c9ea075175cc"), Position = 2, Unconfigured = false });
I expect a dictionary with one item. The key for that item is the first thing object added and the value is the second thing object added.
The intended use of this is to call the replace method:
foreach (var entry in things)
{
entry.Key.Replace(entry.Value);
}
A dictionary may not be an appropriate object for this, I'm open to other suggestions.
Try this solution:
var dic = things.ToDictionary(
t => new Thing
{
ParentKey = t.ParentKey,
Position = t.Position,
Unconfigured = true
},
t => new Thing
{
ParentKey = t.ParentKey,
Position = t.Position,
Unconfigured = false
}
);
I think you may need to resort to a foreach loop, you can try the code below:
List<Thing> things = new List<Thing>();
Dictionary<Thing, Thing> dictionary = new Dictionary<Thing,Thing>();
var keys = from thing in things
where thing.Unconfigured
select thing;
var values = from thing in things
where !thing.Unconfigured
select thing;
foreach (Thing key in keys)
{
var value = (from thing in values
where thing.ParentKey == key.ParentKey &&
thing.Position == key.Position
select thing).FirstOrDefault();
dictionary.Add(key, value);
}
Of course the code assumes that you only have pairs in the List if there may be more than a pair of objects related in the way you described this would need revision
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!
The goal : you have a list of objects, you want the end user to be able to choose some objects properties to define a sort and choose sort direction for each property.
Let's take this Node object as an example:
public class Node
{
public int CustomProperty { get; set; }
public string CustomStringProperty { get; set; }
public string id;
public string parentId;
public string Speed;
public int WheelNumber;
public int SeatNumber;
public List<Node> Child { get; set; }
public Node()
{
Child = new List<Node>();
}
}
Let's say you have a list of Node you want to sort based on criterias such as speed, wheelnumber or seat number.
You will use whatever .OrderBy, .ThenByDescending and so on based on goal but the problem is it's hard coded so it's the developer who is currently managing code; the end user won't dive into code, even less each time he needs to change the criterias collection for sorting and define direction for each criteria (object property).
You could use some code helping the user to define preferences, something like this :
public static List<Node> Sort(List<Node> nodes, int level, string[] sortingPreferences)
{
// Recursively sort nodes children
foreach (var node in nodes)
node.Child = Sort(node.Child, level + 1, sortingPreferences);
// Now sort the nodes based on preference
if (nodes.Count < 2) return nodes;
if (level < sortingPreferences.Length)
{
switch (sortingPreferences[level])
{
case "SPEED": return nodes.OrderBy(node => node.Speed).ToList();
case "WHEEL_NUMBER": return nodes.OrderBy(node => node.WheelNumber).ToList();
case "SEAT_NUMBER": return nodes.OrderBy(node => node.SeatNumber).ToList();
case "SPEED - WHEEL_NUMBER": return nodes.OrderBy(node => node.Speed).ThenBy(node => node.WheelNumber).ToList();
case "SPEED - WHEEL_NUMBER - SEAT_NUMBER": return nodes.OrderBy(node => node.Speed).ThenBy(node => node.WheelNumber).ThenByDescending(node => node.SeatNumber).ToList();
// And so on...
// And so on...
}
}
// Unchanged (or nodes.OrderBy(some default order)
return nodes;
}
This code example is just here to illustrate the idiot approach in there, ie you won't cover all criterias combinations to define your multisort to apply on a list of objects and then extend this combination process to cover also directions for each of the criterias used.
The question is : How to define some sort settings/preferences in which criterias (speed, wheel number, seat number...) would be applied, along with their related sort direction, on a list of object to multisort it ?
I mean, for example, preferences could be given this way :
new List<string>[]
{
new List<string>{ "SPEED", "ASCENDING" },
new List<string>{ "WHEEL_NUMBER", "DESCENDING" },
new List<string>{ "SEAT_NUMBER", "DESCENDING" },
},
Here you have 3 criterias but the end user, through a gui, could add some more and choose direction for each of them.
Then, how to take these preferences into account to apply them on the list of objects?
Create a dictionary of delegates to supply your query with OrderBy clauses. Each delegate must accept an object and return the value to sort on.
var map = new Dictionary<string,Func<Node,IComparable>>
{
{ "PropertyA", node => node.PropertyA },
{ "PropertyB", node => node.PropertyB }
};
Then put your sorting keys into an array as well:
var sortBy = new string[] { "PropertyA", "PropertyB" };
Once you have that in order, you can sort with a simple loop by looking up the delegate for each key. You have to reverse the order of keys, since the each key processed will take precedence over the previous keys.
foreach (var sortKey in sortBy.Reverse())
{
list = list.OrderBy( map[sortKey] );
}
Here's how it looks in context with test data:
var map = new Dictionary<string,Func<Node,IComparable>>
{
{ "PropertyA", node => node.PropertyA },
{ "PropertyB", node => node.PropertyB }
};
IEnumerable<Node> list = new Node[]
{
new Node { PropertyA = 1, PropertyB = "Z" },
new Node { PropertyA = 2, PropertyB = "A" },
new Node { PropertyA = 2, PropertyB = "B" }
};
var sortBy = new string[] { "PropertyA", "PropertyB" };
foreach (var sortKey in sortBy.Reverse())
{
list = list.OrderBy( map[sortKey] );
}
foreach (var node in list)
{
Console.WriteLine("{0} {1}", node.PropertyA, node.PropertyB);
}
Output:
1 Z
2 A
2 B
Working example on DotNetFiddle
Wrap your OrderBy calls into ICommands (command pattern). First you build a commands queue where each command returns a collection, then you loop over the commands queue. If your input changes the commandslist has to be rebuilt.
You can use a fifo queue to store the commands and when an command should be removed you just dequeue until you find the command to be removed while keeping all dequeued commands to then enqueue them minus the removed command back to the queue.
Edit: you can use c# Func for the commands and a queue of keyvaluepair sorting enum and Func.
Func<List<T>,List<T>> for example and Queue<KeyValuePair<SortEnum,Func<List<T>,List<T>>>
I wrote this code to check a Collection to find objects with the same value, but it returns the index -1 and causes an IndexOutOfRangeException. Can anyone help find my mistake?
List<MyFileInfo> selectedItemsList = dataInbox.SelectedItems.Cast<MyFileInfo>().ToList();
foreach (MyFileInfo file in selectedItemsList)
{
if (!file.AdditionalColumn.Equals(""))
{
inDB = new ZeichnungInDB(file.FileInfo.Name, file.AdditionalColumn, file.AdditionalColumn2, file.FileInfo.Extension,
txtAenderungExtern.Text, file.AdditionalColumn3,
int.Parse(txtProjectNumber.Text), txtTag.Text, bemerkung, anhangPfad, cmbDokumententyp.Text, false);
if (zeichnungCollection.Count > 0)
{
if (zeichnungCollection[zeichnungCollection.IndexOf(inDB)].Zeichnungsnummer != inDB.Zeichnungsnummer &&
zeichnungCollection[zeichnungCollection.IndexOf(inDB)].Extension != inDB.Extension)
{
zeichnungCollection.Add(inDB);
}
else
{
sameData = true;
}
}
else
{
zeichnungCollection.Add(inDB);
}
}
}
You are creating a new instance of an object, you are then attempting to find the Index of that object where your collection actually holds reference to a different instance.
You can use FindIndex with ToList to pass in a predicate and find the index of the object where a condition is true.
https://msdn.microsoft.com/en-us/library/x1xzf2ca(v=vs.110).aspx
Alternatively, you could use FirstOrDefault with some null checking if you'd prefer to keep it as an ObservableCollection
https://msdn.microsoft.com/en-us/library/bb340482(v=vs.110).aspx
Assume MyFileInfo looks like this:
public class MyFileInfo
{
public string Name { get; set; }
}
Now try to use it like this:
List<MyFileInfo> selectedItemsList = new List<MyFileInfo>
{
new MyFileInfo { Name = "One" },
new MyFileInfo { Name = "Two" },
};
MyFileInfo two = new MyFileInfo { Name = "Two" };
int index = selectedItemsList.IndexOf(two); // index == -1
IndexOf is looking for identical instance references, which it doesn't find, and so returns -1.
If you do this instead, though, the references are the same:
MyFileInfo two = new MyFileInfo { Name = "Two" };
List<MyFileInfo> selectedItemsList = new List<MyFileInfo>
{
new MyFileInfo { Name = "One" },
two,
};
int index = selectedItemsList.IndexOf(two); // index == 1
This is due to the default implementation of the Equals method, which just compares for reference equality. If you override Equals in MyFileInfo, you get to decide what Equals means. For example:
public class MyFileInfo
{
public string Name { get; set; }
public override bool Equals(object obj)
{
if (obj?.GetType() == typeof(MyFileInfo))
{
return ((MyFileInfo)obj).Name == Name;
}
return false;
}
}
This will find any object with the same Name.
Using methods with predicates is another option, which allows you to define what Equals means on the fly, e.g.:
List<MyFileInfo> selectedItemsList = new List<MyFileInfo>
{
new MyFileInfo { Name = "One" },
new MyFileInfo { Name = "Two" },
};
MyFileInfo two = new MyFileInfo { Name = "Two" };
int index = selectedItemsList.FindIndex(info => info.Name == two.Name);
Which also finds items with the same Name.
Note: If you override Equals in any class that might be used as a dictionary (hash table) key, you should also override GetHashCode. Here's a discussion. And there are considerations for implementing various other interfaces such as IEquatable<T>, especially for structs (value objects), which I guess is out of scope for this question.
Edit: Why it's important to override GetHashCode when overriding Equals
I have IEnumerable collection like following
IEnumerable<Customer> items = new Customer[]
{
new Customer { Name = "test1", Id = 999 },
new Customer { Name = "test2", Id = 989 }
};
I want to get value using key Id
I tried like following
public int GetValue(IEnumerable<T> items,string propertyName)
{
for (int i = 0; i < items.Count(); i++)
{
(typeof(T).GetType().GetProperty(propertyName).GetValue(typeof(T), null));
// I will pass propertyName as Id and want all Id propperty values
// from items collection one by one.
}
}
If you want to retrieve a Customer name from a collection by its Id:
public string GetCustomerName(IEnumerable<Customer> customers, int id)
{
return customers.First(c => c.Id == id).Name;
}
Using LINQ you can get all customers names (values) having specific value in this way:
var valuesList = items.Where(x => x.Something == myVar).Select(v => v.Name).ToList();
For single customer name you can do this:
var singleName = items.FirstOrDefault(x => x.Id == 1)?.Name;
Obviously, the Id can be 1, 2 or any other.
Edit:
I recommend you List<Customer> instead of Customer[]
So,
var items = new List<Customer>
{
new Customer { Name = "test1", Id = 999 },
new Customer { Name = "test2", Id = 989 }
};
// I will pass propertyName as Id and want all Id propperty values
// from items collection one by one.
If I understand you correctly
public static IEnumerable<object> GetValues<T>(IEnumerable<T> items, string propertyName)
{
Type type = typeof(T);
var prop = type.GetProperty(propertyName);
foreach (var item in items)
yield return prop.GetValue(item, null);
}
Just use LINQ to achieve what you want to do. if you want to retrieve a specific value you can use where like this:
public Customer GetCustomerById(IEnumerable<Customer> items,int key)
{
return items.Where(x=>x.id==key)
.Select(x =>x.Name)
.First();
}
this will retrieve the customer who match a specific Id.
Do you want to look things up repeatedly after creating the list? If so, you might want to consider creating a dictionary to do the lookups, like so:
IEnumerable<Customer> items = new Customer[]
{
new Customer {Name = "test1", Id = 999},
new Customer {Name = "test2", Id = 989}
};
var lookup = items.ToDictionary(itemKeySelector => itemKeySelector.Id);
var result = lookup[989];
Console.WriteLine(result.Name); // Prints "test2".
I'm assuming that you don't create the collection in the first place - if you had control over creating the original collection you could use a dictionary in the first place.
private TextBox [] Collectionstextboxonpanel(Panel panel)
{
var textBoxspanel1 = panel.Controls.OfType<TextBox>(); // select controls on panle1 by type
IEnumerable<TextBox> textBoxes = textBoxspanel1; // create collection if need
TextBox[] textBoxes1 = textBoxes.ToArray(); // Array collection
return textBoxes1; // get back TextBox Collection
}
Basically I'm looking to select both string columns and put it all into a single array of strings. Right now I'm having to do two selects and combine the results. It isn't a huge deal, I just think it looks awkward. Any suggestions on how to accomplish the same goal with one linq statement? Here is a test case I'm using to mess around:
[TestFixture]
public class test {
public class Values {
public string Present { get; set; }
public string Previous { get; set; }
public bool Flag { get; set; }
}
[Test]
public void test1() {
var list = new List<Values> {
new Values { Present = "present1", Previous = "previous1", Flag = false },
new Values { Present = "present2", Previous = "previous2", Flag = false },
new Values { Present = "present3", Previous = "previous3", Flag = true },
new Values { Present = "present4", Previous = "previous4", Flag = true }
};
var r1 = list.Where(c => c.Flag).Select(c => c.Present);
var r2 = list.Where(c => c.Flag).Select(c => c.Previous);
var combined = r1.Concat(r2);
Assert.AreEqual(4, combined.Count());
}
}
An alternative solution with using SelectMany (it keeps the duplicates):
var combined = list.Where(c => c.Flag)
.SelectMany(c => new[] { c.Present, c.Previous });
Assert.AreEqual(4, combined.Count());
Does the final ordering of the strings in the list matter? If not, it seems like this would be the clearest way to accomplish this:
var strings = new List<String>();
foreach (var value in list.Where(c => c.Flag))
{
strings.Add(value.Present);
strings.Add(value.Previous);
}