idictionary help filtering items - c#

Is there a better way to code the Where this:
IDictionary<string, string> p = new Dictionary<string, string>();
p.Add("Apple", "1");
p.Add("Orange", "2");
p.Add("Pear", "3");
p.Add("Grape", "4");
p.Add("Pineapple", "5");
//This is a unique list
var retVal = p.Where(k => k.Key.Contains("Apple") || k.Key.Contains("Pear") || k.Key.Contains("Grape"));
Some History Below
I have a dictionary of strings like the following:
IDictionary<string,string>
The contents look like this:
Apple,1
Orange,2
Pear,3
Grape,4
...many more
How do i return only a few items from my dictionary like so
if (true)
{
//return only 3 apple,pear&grape items out of the dozens in the list into a new variable
}

You can just take the first 3 items...
theDictionary.Take(3);
Or filter and take specific items...
string[] itemsIWant = { "Apple", "Pear", "Grape" };
theDictionary.Where(o => itemsIWant.Contains(o.Key));
Or sort randomly and take 3...
Random r = new Random();
theDictionary.OrderBy(o => r.Next()).Take(3);

check out
LINQ query to return a Dictionary<string, string>

That will really depend on the kind of filtering you want to achieve. But you can achieve it through Linq.
If you just want to get the first 3 items, you can do it like this:
theDictionary.Take(3);
If you want to get the first 3 items that begin with 'G', the you will do this:
theDictionary.Where(kv => kv.Key.StartsWith("G")).Take(3);
If you want to get the first 3 items that begin with 'G' regardless the casing, the you will do this:
theDictionary.Where(kv => kv.Key.ToLower().StartsWith("g")).Take(3);
Last, but not least, if you want to get 3 items randomly, you will do this:
Random rand = new Random();
theDictionary.OrderBy(kv => rand.Next()).Take(3);

Related

C# Join two List<int>, remove duplicates, NO LINQ

This is the idea: I have two List<int> and I want to make a third List<int> with the above mentioned lists joined, without duplicates. I know how to use .Union but I want to make this without using LINQ. So far I have this:
Console.WriteLine("Enter numbers for first list: ");
List<int> firstList = new List<int>{20, 40, 10, 10, 30, 80};
//Console.ReadLine().Split(' ').Select(int.Parse).ToList();
Console.WriteLine("Enter numbers for second list: ");
List<int> secondList = new List<int> {25, 20, 40, 30, 10 };
//Console.ReadLine().Split(' ').Select(int.Parse).ToList();
List<int> newList = new List<int>();
foreach (var item in firstList)
{
if (secondList.Contains(item))
{
continue;
}
}
newList.Sort();
newList.ForEach(p => Console.WriteLine(p));
And I am actually stuck...I think that I need to iterate each one of the lists and if the items are equal, add them just once to the new list...But I can't seem to figure out how to do that if the lists are different count.
Any ideas?
This is presented with a big (and I do mean big) caveat - it's going to be slow. You will get much better performance from using LINQ or a different collection (eg. HashSet). This approach is O(n^2) whereas LINQ etc. is O(n).
Simply loop over the second list adding the value to the first if it's not already in the list.
foreach (var item in secondList)
{
if (!firstList.Contains(item))
{
firstList.Add(item);
}
}
Given that you want a new list at the end of the process you can just add all the items from the first list to the result before the above code:
foreach (var item in firstList)
{
newList.Add(item);
}
and replace firstList with newList when adding.
You could take advantage of different types of collections to do the following:
var set = new HashSet<int>(firstList);
set.UnionWith(secondList);
var newList = new List<int>(set);
Something like this?
newList.AddRange(firstList);
newList.AddRange(secondList);
newList = newList.Distinct().ToList();
newList.Sort();

List function, how to get an average of scores for each name- c# console application

I have a list function on a console application on C#. This list function has different items where they look something like 'matt,5' 'matt,7' 'jack,4' 'jack,8' etc...
I want to be able to combine all of the names where I only see their name written once but the number after them are averaged out so it would be like 'jack,5+7/2' which would then display as 'jack,6'.
So far I have this...
currentFileReader = new StreamReader(file);
List<string> AverageList = new List<string>();
while (!currentFileReader.EndOfStream)
{
string text = currentFileReader.ReadLine();
AverageList.Add(text.ToString());
}
AverageList.GroupBy(n => n).Any(c => c.Count() > 1);
Not really sure where to go from here.
What you need is to Split your each string item on , and then group by first element of the returned array and average second element of the array (after parsing it to int) something like:
List<string> AverageList = new List<string> { "matt,5", "matt,7", "jack,4", "jack,8" };
var query = AverageList.Select(s => s.Split(','))
.GroupBy(sp => sp[0])
.Select(grp =>
new
{
Name = grp.Key,
Avg = grp.Average(t=> int.Parse(t[1])),
});
foreach (var item in query)
{
Console.WriteLine("Name: {0}, Avg: {1}", item.Name, item.Avg);
}
and it will give you:
Name: matt, Avg: 6
Name: jack, Avg: 6
But, a better option would be to use a class with Name and Score properties instead of comma separated string values.
(The code above doesn't check for invalid input values).
Firstly you will want to populate your unformatted data into a List, as you can see I called it rawScores. You could then Split each line by the comma delimiting them. You can then check to see if an existing person is in your Dictionary and add his score to it, or if not create a new person.
After that you would simply have to generate the Average of the List.
Hope this helps!
var scores = new Dictionary<string, List<int>>();
var rawScores = new List<string>();
rawScores.ForEach(raw =>
{
var split = raw.Split(',');
if (scores.Keys.All(a => a != split[0]))
{
scores.Add(split[0], new List<int> {Convert.ToInt32(split[1])});
}
else
{
var existing = scores.FirstOrDefault(f => f.Key == split[0]);
existing.Value.Add(Convert.ToInt32(split[1]));
}
});

In C#, What is the fastest way to search for elements in a list but do a "StartsWith()" search?

I have a list of strings:
var list = new List<string>();
list.Add("CAT");
list.Add("DOG");
var listofItems = new List<string>();
listofItems .Add("CATS ARE GOOD");
listofItems .Add("DOGS ARE NICE");
listofItems .Add("BIRD");
listofItems .Add("CATAPULT");
listofItems .Add("DOGGY");
and now i want a function like this:
listofItems.Where(r=> list.Contains(r));
but instead of Contains, i want it to do a starts with check so 4 out of the 5 items would be returned (BIRD would NOT).
What is the fastest way to do that?
You can use StartsWith inside of an Any
listofItems.Where(item=>list.Any(startsWithWord=>item.StartsWith(startsWithWord)))
You can visualize this as a double for loop, with the second for breaking out as soon as it hits a true case
var filteredList = new List<String>();
foreach(var item in listOfItems)
{
foreach(var startsWithWord in list)
{
if(item.StartsWith(startsWithWord))
{
filteredList.Add(item)
break;
}
}
}
return filteredList;
The fastest way would be usage of another data structure, for example Trie. Basic C# implementation can be found here: https://github.com/kpol/trie
This should get you what you need in a more simplified format:
var result = listofItems.Select(n =>
{
bool res = list.Any(v => n.StartsWith(v));
return res
? n
: string.Empty;
}).Where(b => !b.Equals(string.Empty));
The Trie data structure is what you need. Take a look at this more mature library: TrieNet
using Gma.DataStructures.StringSearch;
...
var trie = new SuffixTrie<int>(3);
trie.Add("hello", 1);
trie.Add("world", 2);
trie.Add("hell", 3);
var result = trie.Retrieve("hel");

get unique values from query to build Dictionary

I want to build a combobox with key->postal and value->city to use as filter for my accomodations.
To limit the number of items in the list I only use the postals I have used when filling up the table tblAccomodations.
For now I do not use a relational table with postals and city's although I'm thinking about an update later on.
Here I build my dictionary:
public static Dictionary<int, string> getPostals()
{
Dictionary<int, string> oPostals = new Dictionary<int, string>();
using (DBReservationDataContext oReservation = new DBReservationDataContext())
{
var oAllPostals = (from oAccomodation in oReservation.tblAccomodations
orderby oAccomodation.Name ascending
select oAccomodation);
foreach (tblAccomodation item in oAllPostals)
{
oPostals.Add(int.Parse(item.Postal), item.City);
}
}
return oPostals;
}
As expected I got an error: some Accomodations are located in the same city, so there are double values for the key. So how can I get a list of unique cities and postals (as key)?
I tried to use
select oAccomodation.Postal.Distinct()
but that didn't work either.
UPDATE: I have found the main problem. There are multiple cities with the same postal ("Subcity"). So I'm gonna filter on "City" and not on "Postal".
I think your looking for 'Distinct'. Gather your list of all postals and then return myPostals.Distinct().
Hope than helps.
change
foreach (tblAccomodation item in oAllPostals)
{
oPostals.Add(int.Parse(item.Postal), item.City);
}
to
foreach (tblAccomodation item in oAllPostals.Distinct(x=>x..Postal)
{
if(!oPostals.ContainsKey(int.Parse(item.Postal)))
oPostals.Add(int.Parse(item.Postal), item.City);
}
BTW, if you have multiple cities in one postal (I am not sure if it is possible in your domain), which one you want to see?
If any of cities will do, then it is easy to just get the first one per postal:
var oAllPostals = oReservation.tblAccomodations
.OrderBy(x=>x.Name)
.ToLookup(x=>x.Postal, x=>x.City)
.ToDictionary(x=>x.Key, x.First());
In the same example if you do .ToList() or even .Distinct().ToList() instead of .First() you will have all of cities in the dictionary of Dictionary<Postal, List<City>>.
Assuming the combination of postal + city is unique you could do the following:
public static Dictionary<int, string> getPostals()
{
Dictionary<int, string> oPostals = new Dictionary<int, string>();
using (DBReservationDataContext oReservation = new DBReservationDataContext())
{
var oAllPostals = (from oAccomodation in oReservation.tblAccomodations
orderby oAccomodation.Name ascending
select oAccomodation);
foreach (tblAccomodation item in oAllPostals)
{
oPostals.Add((item.Postal + item.City).GetHashCode(), item.Postal + " " + item.City);
}
}
return oPostals;
}
Edit:
If you want to use the selected value from the drop box then you can use the following:
public static Dictionary<int, Tuple<string, string>> getPostals()
{
Dictionary<int, string> oPostals = new Dictionary<int, string>();
using (DBReservationDataContext oReservation = new DBReservationDataContext())
{
var oAllPostals = (from oAccomodation in oReservation.tblAccomodations
orderby oAccomodation.Name ascending
select oAccomodation);
foreach (tblAccomodation item in oAllPostals)
{
oPostals.Add((item.Postal + item.City).GetHashCode(), new Tuple<string, string>(item.Postal, item.City));
}
}
return oPostals;
}
The way you bind the following depends on whether you're using asp.net, winforms etc. Here's an example for winforms.
Using .containkey will exclude [1 (postal key) to n (cities relation)]. i.e since Key already exists next city (with the same postal key ) will not get into your dictionary.
However, if you want to map your postal to list of cities, you can represent a dictionary that can contain a collection of values like the following:
Dictionary < String[Postal]> , List < Cities>>
This way you'll have a dictionary that can have multiple values.

Creating a random name generator. How do I accomplish this?

I'm trying to grab a single item from each of the Lists here, and combine them to make a unique name. This is just for kicks. :)
Here are the lists:
List<string> FirstNames = new List<string>()
{
"Sergio",
"Daniel",
"Carolina",
"David",
"Reina",
"Saul",
"Bernard",
"Danny",
"Dimas",
"Yuri",
"Ivan",
"Laura"
};
List<string> LastNamesA = new List<string>()
{
"Tapia",
"Gutierrez",
"Rueda",
"Galviz",
"Yuli",
"Rivera",
"Mamami",
"Saucedo",
"Dominguez",
"Escobar",
"Martin",
"Crespo"
};
List<string> LastNamesB = new List<string>()
{
"Johnson",
"Williams",
"Jones",
"Brown",
"David",
"Miller",
"Wilson",
"Anderson",
"Thomas",
"Jackson",
"White",
"Robinson"
};
I know I get a single item via an index, and I also know that I can use the Random class to generate a random number from 0 to ListFoo.Count.
What I don't know is how to check if a random permutation has already been drawn from the collections.
I've thought about using the tuple class:
List<Tuple<int,int,int>> permutations = new List<Tuple<int,int,int>>();
But I'm having a brainfart here. ;) Any guidance? I'm not really looking for the entire code to this simple problem, just a suggestion or hint.
EDIT
Thanks to the suggestions given here, here what I've come up with. Any room for improvements?
static void Main(string[] args)
{
List<string> FirstNames = new List<string>()
{
"Sergio",
"Daniel",
"Carolina",
"David",
"Reina",
"Saul",
"Bernard",
"Danny",
"Dimas",
"Yuri",
"Ivan",
"Laura"
};
List<string> LastNamesA = new List<string>()
{
"Tapia",
"Gutierrez",
"Rueda",
"Galviz",
"Yuli",
"Rivera",
"Mamami",
"Saucedo",
"Dominguez",
"Escobar",
"Martin",
"Crespo"
};
List<string> LastNamesB = new List<string>()
{
"Johnson",
"Williams",
"Jones",
"Brown",
"David",
"Miller",
"Wilson",
"Anderson",
"Thomas",
"Jackson",
"White",
"Robinson"
};
var permutations = new List<Tuple<int, int, int>>();
List<string> generatedNames = new List<string>();
Random random = new Random();
int a, b, c;
//We want to generate 500 names.
while (permutations.Count < 500)
{
a = random.Next(0, FirstNames.Count);
b = random.Next(0, FirstNames.Count);
c = random.Next(0, FirstNames.Count);
Tuple<int, int, int> tuple = new Tuple<int, int, int>(a, b, c);
if (!permutations.Contains(tuple))
{
permutations.Add(tuple);
}
}
foreach (var tuple in permutations)
{
generatedNames.Add(string.Format("{0} {1} {2}", FirstNames[tuple.Item1],
LastNamesA[tuple.Item2],
LastNamesB[tuple.Item3])
);
}
foreach (var n in generatedNames)
{
Console.WriteLine(n);
}
Console.ReadKey();
}
You are on the right track!
Every time you generate a name, add it to your tuple list
//Create the tuple
Tuple <int, int, int> tuple = new Tuple<int, int, int>(index1, index2, index3)
if(!permutations.Contains(tuple))
{
permutations.Add(tuple);
//Do something else
}
I would think the simplest solution is to just the stuff the assembled name into a HashSet<string> which will ensure the list of created names is unique.
An alternative to the HashSet answer is to build all of the possible combinations in advance, shuffle them, then store them in a Queue, where you can retrieve them one at a time. This will avoid having to check the existing ones every time you build a new one, and will still be random.
This only works if you don't have a large set to begin with, since the work involved in creating the complete list and shuffling it would be huge for a large set of data.
It's really easy to generate them all using LINQ:
var combs =
(from first in FirstNames
from second in LastNamesA
from third in LastNamesB
select new Tuple<string, string, string>(first, second, third)).ToList();
After this, if you need to take unique elements from the list randomly, just shuffle the list and then pick them one-by-one in order.
You can use the Knuth-Fisher-Yates algorithm (that's an in-place shuffle):
Random rand = new Random();
for (int i = combs.Count - 1; i > 0; i--)
{
int n = rand.Next(i + 1);
var mem = combs[i];
combs[i] = combs[n];
combs[n] = mem;
}
I would create a HashSet<int> and store the numeric representation of the picks (eg 135 for first, third and 5th or use 010305) and then check if they are in the set.
Create a new tuple with 3 random digits
Check if permutations contains your new tuple
If not => Add new tuple to the list. If yes, start with point 1 again.

Categories

Resources