C# 2 string arrays to List<string, string> - c#

Assuming the 2 string arrays are the same length and not empty how can I make a List of the contents?
I had a Dictionary working, but now I need to be able to use duplicate keys so I am resorting to a List.
string[] files = svd.file.Split(",".ToCharArray());
string[] references = svd.references.Split(",".ToCharArray());
Dictionary<string, string> frDictionary = new Dictionary<string, string>();
frDictionary = files.Zip(rReferences, (s, i) => new { s, i })
.ToDictionary(item => item.s, item => item.i);
I could do it like:
List<string, string> jcList = new List<string, string>();
and then just have a double loop into the two arrays but I know a faster way must exist.

ILookup<string,string> myLookup =
files.Zip(rReferences, (s, i) => new { s, i })
.ToLookup(item => item.s, item => item.i);
will create a Dictionary-like structure that allows multiple values per key.
So
IEnumerable<string> foo = myLookup["somestring"];

A List containing elements with two strings each is simplest implemented with a
List<T>
and
T == Tuple<string,string>
Then use a loop to build your list from the two arrays:
string[] s1 =
{
"1", "2"
};
string[] s2 =
{
"a", "b"
};
var jcList = new List<Tuple<string,string>>();
for (int i = 0; i < s1.Length; i++)
{
jcList.Add(Tuple.Create(s1[i], s2[i]));
}
or using LINQ:
var jcList = s1.Select((t, i) => Tuple.Create(t, s2[i])).ToList();

Related

Splitting an array into 2D or two arrays

I have a text file that I am reading to pull out register values and what they contain.
2 lines from the array pulled out is: (full list is about 700)
0x0003 = 0x0069
0x0007 = 0x0078
I would like to split these into two arrays or one 2-dimensional array, whatever is best(I am new to using arrays)
My goal is to search the array for example for register 3, find the index then extract the information from the 2nd arrays corresponding index.
Here is my code so far ,
List<string> registerFullList1 = new List<string>();
for (int i = 0; i < 2000; i++)
{
string[] importStringArray1 = new string[2000];
importStringArray1[i] = importStringArray1[i] + objReader.ReadLine() + "\r\n";
//code to extract register info from string array
string listsplit1 = Regex.Match(importStringArray1[i], #"(?<= 0x)[0-9A-Fa-z\s\=]{13}").Value;
if (listsplit1.Contains("0x")) //code to add to list only registers and ignore empty lines
{
registerFullList1.Add(Convert.ToString(listsplit1));
}
}
int[] index = new int[2000]; // is there a way here that I don't have to assign 0,1,2,3 to each assignment?
index[0] = registerFullList1.FindIndex(x => x.StartsWith("0003 ="));
Register3.Text = Regex.Match(registerFullList1[index[0]], #"(?<= 0x)[0-9A-F]{4}").Value;
index[1] = registerFullList1.FindIndex(x => x.StartsWith("0007 ="));
Register7.Text = Regex.Match(registerFullList1[index[1]], #"(?<= 0x)[0-9A-F]{4}").Value;
This all works no problem and I am displaying the register content in text boxes. But I would like two arrays so it is more proper, one with register numbers and one with content. I cant figure it out, any help would be appreciated.
UPDATE
final code after reading answers,
List<string> registerNumberList = new List<string>();
List<string> registerContentList = new List<string>();
List<string> registerFullList = new List<string>();
for (int i = 0; i < 2000; i++)
{
string[] importStringArray1 = new string[2000];
importStringArray1[i] = importStringArray1[i] + objReader.ReadLine() + "\r\n";
string listsplit1 = Regex.Match(importStringArray1[i], #"(?<= 0x)[0-9A-Fa-z\s\=]{13}").Value; // #"(?<== 0x)[0-9A-F]{4}"
string listsplit2 = Regex.Match(importStringArray1[i], #"(?<= 0x)[0-9A-Fa-z\s\=]{4}").Value;// pulls out the register number from original array
string listsplit3= Regex.Match(importStringArray1[i], #"(?<== 0x)[0-9A-Fa-z\s\=]{4}").Value;//pulls out register content from original array
if (listsplit1.Contains("0x"))
{
registerNumberList.Add(Convert.ToString(listsplit3));//makes a list with register numbers
registerContentList.Add(Convert.ToString(listsplit2) );//makes a list with register content
registerFullList.Add(Convert.ToString(listsplit2) + "=" + Convert.ToString(listsplit3));//the full list
}
}
Dictionary <string, string> registers = registerFullList.Select(line => line.Split('=')
.ToArray())
.ToDictionary(items => items[0], items => items[1]); //joins the register numbers and content into a dictionary with just 4 decimal values for each
list1.Text = String.Join("\r\n", registerFullList);
list2.Text = registers["0010"]; // pulls out register info
Thanks guys
I suggest using Linq:
If you insist on the array:
int[][] result = File
.ReadLines(#"C:\myFile.txt")
.Select(line => line
.Split('=')
.Select(item => Convert.ToInt32(item, 16))
.ToArray())
.ToArray();
In case first index is unique one (and thus can serve as a key) you can materialize the data as a dictionary:
Dictionary<int, int> result = File
.ReadLines(#"C:\myFile.txt")
.Select(line => line
.Split('=')
.Select(item => Convert.ToInt32(item, 16))
.ToArray())
.ToDictionary(items => items[0], items => items[1]);
...
// value == 0x0078 (120)
int value = result[0x0007];
Try using a dictionary :
Dictionary<string, string> registersKeyValue = new Dictionary<string, string>();
string content = registersKeyValue
.Where(keyValuePair => keyValuePair.Key == "0x0003")
.FirstOrDefault()
.Value;

List to Dictionary with incremental keys in one line LINQ statement

I have this list:
var items = new List<string>() { "Hello", "I am a value", "Bye" };
I want it to convert it to a dictionary with the following structure:
var dic = new Dictionary<int, string>()
{
{ 1, "Hello" },
{ 2, "I am a value" },
{ 3, "Bye" }
};
As you can see, the dictionary keys are just incremental values, but they should also reflect the positions of each element in the list.
I am looking for a one-line LINQ statement. Something like this:
var dic = items.ToDictionary(i => **Specify incremental key or get element index**, i => i);
You can do that by using the overload of Enumerable.Select which passes the index of the element:
var dic = items.Select((val, index) => new { Index = index, Value = val})
.ToDictionary(i => i.Index, i => i.Value);
static void Main(string[] args)
{
var items = new List<string>() { "Hello", "I am a value", "Bye" };
int i = 1;
var dict = items.ToDictionary(A => i++, A => A);
foreach (var v in dict)
{
Console.WriteLine(v.Key + " " + v.Value);
}
Console.ReadLine();
}
Output
1 Hello
2 I am a value
3 Bye
EDIT: Out of curosity i did a performance test with a list of 3 million strings.
1st Place: Simple For loop to add items to a dictionary using the loop count as the key value. (Time: 00:00:00.2494029)
2nd Place: This answer using a integer variable outside of LINQ. Time(00:00:00.2931745)
3rd Place: Yuval Itzchakov's Answer doing it all on a single line. Time (00:00:00.7308006)
var items = new List<string>() { "Hello", "I am a value", "Bye" };
solution #1:
var dic2 = items.Select((item, index) => new { index, item })
.ToDictionary(x => x.item, x => x.index);
solution #2:
int counter = 0;
var dic = items.ToDictionary(x => x, z => counter++);

how to find members that exist in at least two lists in a list of lists

I have an array of lists:
var stringLists = new List<string>[]
{
new List<string>(){ "a", "b", "c" },
new List<string>(){ "d", "b", "c" },
new List<string>(){ "a", "d", "c" }
};
I want to extract all elements that are common in at least 2 lists. So for this example, I should get all elements ["a", "b", "c", "d"]. I know how to find elements common to all but couldn't think of any way to solve this problem.
You could use something like this:
var result = stringLists.SelectMany(l => l.Distinct())
.GroupBy(e => e)
.Where(g => g.Count() >= 2)
.Select(g => g.Key);
Just for fun some iterative solutions:
var seen = new HashSet<string>();
var current = new HashSet<string>();
var result = new HashSet<string>();
foreach (var list in stringLists)
{
foreach(var element in list)
if(current.Add(element) && !seen.Add(element))
result.Add(element);
current.Clear();
}
or:
var already_seen = new Dictionary<string, bool>();
foreach(var list in stringLists)
foreach(var element in list.Distinct())
already_seen[element] = already_seen.ContainsKey(element);
var result = already_seen.Where(kvp => kvp.Value).Select(kvp => kvp.Key);
or (inspired by Tim's answer):
int tmp;
var items = new Dictionary<string,int>();
foreach(var str in stringLists.SelectMany(l => l.Distinct()))
{
items.TryGetValue(str, out tmp);
items[str] = tmp + 1;
}
var result = items.Where(kv => kv.Value >= 2).Select(kv => kv.Key);
You could use a Dictionary<string, int>, the key is the string and the value is the count:
Dictionary<string, int> itemCounts = new Dictionary<string,int>();
for(int i = 0; i < stringLists.Length; i++)
{
List<string> list = stringLists[i];
foreach(string str in list.Distinct())
{
if(itemCounts.ContainsKey(str))
itemCounts[str] += 1;
else
itemCounts.Add(str, 1);
}
}
var result = itemCounts.Where(kv => kv.Value >= 2);
I use list.Distinct() since you only want to count occurences in different lists.
As requested, here is an extension method which you can reuse with any type:
public static IEnumerable<T> GetItemsWhichOccurAtLeastIn<T>(this IEnumerable<IEnumerable<T>> seq, int minCount, IEqualityComparer<T> comparer = null)
{
if (comparer == null) comparer = EqualityComparer<T>.Default;
Dictionary<T, int> itemCounts = new Dictionary<T, int>(comparer);
foreach (IEnumerable<T> subSeq in seq)
{
foreach (T x in subSeq.Distinct(comparer))
{
if (itemCounts.ContainsKey(x))
itemCounts[x] += 1;
else
itemCounts.Add(x, 1);
}
}
foreach(var kv in itemCounts.Where(kv => kv.Value >= minCount))
yield return kv.Key;
}
Usage is simple:
string result = String.Join(",", stringLists.GetItemsWhichOccurAtLeastIn(2)); // a,b,c,d
Follow these steps:
Create a Dictionary element -> List of indices
loop over all lists
for list number i: foreach element in the list: add i to the list in the dictionary at position : dictionary[element].Add(i) (if not already present)
Count how many lists in the dictionary have two entries
You can use SelectMany to flatten the list and then pick all elemeents which occur twice or more:
var singleList = stringLists.SelectMany(p => p);
var results = singleList.Where(p => singleList.Count(q => p == q) >= 2).Distinct();

Dictionary<string, string> Value lookup performance

I am working on a small project but have run into a performance roadblock.
I have a Dictionary<string, string>()
I have a string[].
Lets say my Dictionary has 50,000 entries, and my string[] has 30,000 entries.
I want to collect the Keys from my Dictionary where the value.ToCharArray().OrderBy(x => x) equals a value.ToCharArray().OrderBy(x => x) of my string[].
I have tried reducing the number of KeyValue pairs I have to look through by comparing the length of my string[] value to the values in the Dictionary, but that has not really gained me any performance.
Does anyone have an ideas how I can improve the performance of this lookup?
Thanks!
To expand the pseudocode:
var stringToLookUp = GetSomeStrings(s.ToString()).Select(x => x).OrderBy(x => x).ToArray();
var aDictionaryOfStringString = GetDictionary(Resources.stringList);
var results = new List<string>();
foreach (var theString in stringToLookUp.Where(aString=> aString.Length > 0))
{
if (theString.Length > 0)
{
var theStringClosure = theString;
var filteredKeyValuePairs = aDictionaryOfStringString.Where(w => w.Value.Length == theStringClosure.Length && !results.Contains(w.Key)).ToArray();
var foundStrings = filteredKeyValuePairs.Where(kv => kv.Value.ToCharArray().OrderBy(c => c).ToArray().SequenceEqual(theStringClosure))
.Select(kv => kv.Key)
.ToArray();
if (foundStrings.Any()) results.AddRange(foundStrings);
}
}
I think principal problem is you iterate over whole dictionary in every single iteration - this is O(N^2). Better build hashset based on your modified key (either from dictionary or from array) and iterate over the second. This is O(N).
// some values
var dictionary = new Dictionary<string, string>();
var fields = new string[]{};
string[] modifiedFields = new string[fields.Length];
for(var i =0; i < fields.Length; i++)
{
modifiedFields[i] = new string(fields[i].ToCharArray().OrderBy(x =>x).ToArray());
}
var set = new HashSet<string>(modifiedFields);
var results = new List<string>();
foreach(var pair in dictionary)
{
string key = new string(pair.Value.ToCharArray().OrderBy(x =>x).ToArray());
if (set.Contains(key))
{
results.Add(pair.Key);
}
}
You can try this
var stringToLookUp = GetSomeStrings(s.ToString()).Select(x => x).OrderBy(x => x).ToArray();
var aDictionaryOfStringString = GetDictionary(Resources.stringList);
var results = aDictionaryOfStringString.Where(kvp => stringToLookUp.Select(s => s.OrderBy(x => x)).Contains(kvp.Value.OrderBy(x => x))).Select(kvp => kvp.Key).ToList();

How to check for duplicates in an array and then do something with their values?

I have an array for example("1:2","5:90","7:12",1:70,"29:60") Wherein ID and Qty are separated by a ':' (colon), what I want to do is when there's a duplicate of IDs the program will add the qty and return the new set of arrays so in the example it will become ("1:72","5:90","7:12","29:60").
Ex.2 ("1:2","5:90","7:12","1:70","29:60","1:5") becomes ("1:77","5:90","7:12","29:60").
I want to solve it without using linq.
var foo = array.Select(s => s.Split(':'))
.GroupBy(x => x[0])
.Select(g =>
String.Format(
"{0}:{1}",
g.Key,
g.Sum(x => Int32.Parse(x[1]))
)
)
.ToArray();
Note, it's not necessary to parse the "keys," only the values.
Without LINQ:
var dictionary = new Dictionary<string, int>();
foreach (var group in array) {
var fields = group.Split(':');
if (!dictionary.ContainsKey(fields[0])) {
dictionary.Add(fields[0], 0);
}
dictionary[fields[0]] += Int32.Parse(fields[1]);
}
string[] foo = new string[dictionary.Count];
int index = 0;
foreach (var kvp in dictionary) {
foo[index++] = String.Format("{0}:{1}", kvp.Key, kvp.Value);
}
You have to do this manually. Loop through each list, check the ID for each element. Put it in a Dictionary<int, int>, Dictionary<id, qt>. If the dictionary contains the id, add it to the value.
Loop, add, check using Dictionary class.
If you want it without LINQ...
var totalQuantities = new Dictionary<int, int>();
foreach(var raw in sourceArr) {
var splitted = raw.Split(':');
int id = int.Parse(splitted[0]);
int qty = int.Parse(splitted[1]);
if(!totalQuantities.ContainsKey(id)) {
totalQuantities[id] = 0;
}
totalQuantities[id] += qty;
}
var result = new string[totalQuantities.Count];
int i=0;
foreach(var kvp in totalQuantities) {
result[i] = string.Format("{0}:{1}", kvp.Key, kvp.Value);
i++;
}
(
from raw in arr
let splitted = raw.Split(':')
let id = int.Parse(splitted[0])
let qty = int.Parse(splitted[1])
let data = new { id, qty }
group data by data.id into grp
let totalQty = grp.Sum(val => val.qty)
let newStr = string.Format("{0}:{1}", grp.Key, totalQty
select newStr
)
.ToArray()
Note that the code may contain accidental errors, as it was written in notepad.
var input=new string[]{"1:2","5:90","7:12","1:70","29:60","1:5"};
var result=input
.Select(s=>s.Split(':'))
.Select(x=>x.Select(s=>int.Parse(s)).ToArray())
.GroupBy(x=>x[0])
.Select(g=>g.Key+":"+g.Sum(x=>x[1]));
I was too lazy to specify the culture everywhere. You probably want to do that before putting it into production, or it will fail for cultures with unusual integer representations.
var totals=new Dictionary<int,int>
foreach(string s in input)
{
string[] parts=s.Split(':');
int id=int.Parse(parts[0]);
int quantity=int.Parse(parts[0]);
int totalQuantity;
if(!totals.TryGetValue(id,out totalQuantity))
totalQuantity=0;//Yes I know this is redundant
totalQuanity+=quantity;
totals[id]=totalQuantity;
}
var result=new List<string>();
foreach(var pair in totals)
{
result.Add(pair.Key+":"+pair.Value);
}
try this:
List<string> items = new List<string>(new string[] { "1:2", "5:90", "7:12", "1:70", "29:60" });
Dictionary<string, int> dictionary = new Dictionary<string, int>();
foreach (string item in items)
{
string[] data = item.Split(':');
string key = data[0];
if (!dictionary.ContainsKey(data[0]))
{
int value = dictionary[data[0]];
dictionary[key] += int.Parse(data[1]);
}
}
//Used dictionary values here

Categories

Resources