Related
I'm trying to remove the duplicate combination from a csv file.
I tried using Distinct but it seems to stay the same.
string path;
string newcsvpath = #"C:\Documents and Settings\MrGrimm\Desktop\clean.csv";
OpenFileDialog openfileDial = new OpenFileDialog();
if (openfileDial.ShowDialog() == DialogResult.OK)
{
path = openfileDial.FileName;
var lines = File.ReadLines(path);
var grouped = lines.GroupBy(line => string.Join(", ", line.Split(',').Distinct())).ToArray();
var unique = grouped.Select(g => g.First());
var buffer = new StringBuilder();
foreach (var name in unique)
{
string value = name;
buffer.AppendLine(value);
}
File.WriteAllText(newcsvpath ,buffer.ToString());
label5.Text = "Complete";
}
For example, I have a combination of
{ 1,1,1,1,1,1,1,1 } { 1,1,1,1,1,1,1,2 }
{ 2,1,1,1,1,1,1,1 } { 1,1,1,2,1,1,1,1 }
The output should be
{ 1,1,1,1,1,1,1,1 }
{ 2,1,1,1,1,1,1,1 }
From you example, it seems that you want to treat each line as a sequence of numbers and that you consider two lines equal if one sequence is a permutation of the other.
So from reading your file, you have:
var lines = new[]
{
"1,1,1,1,1,1,1,1",
"1,1,1,1,1,1,1,2",
"2,1,1,1,1,1,1,1",
"1,1,1,2,1,1,1,1"
};
Now let's convert it to an array of number sequences:
var linesAsNumberSequences = lines.Select(line => line.Split(',')
.Select(int.Parse)
.ToArray())
.ToArray();
Or better, since we are not interested in permutations, we can sort the numbers in the sequences immediately:
var linesAsSortedNumberSequences = lines.Select(line => line.Split(',')
.Select(int.Parse)
.OrderBy(number => number)
.ToArray())
.ToArray();
When using Distinct on this, we have to pass a comparer which considers two array equal, if they have the same elements. Let's use the one from this SO question
var result = linesAsSortedNumberSequences.Distinct(new IEnumerableComparer<int>());
Try it
HashSet<string> record = new HashSet<string>();
foreach (var row in dtCSV.Rows)
{
StringBuilder textEditor= new StringBuilder();
foreach (string col in columns)
{
textEditor.AppendFormat("[{0}={1}]", col, row[col].ToString());
}
if (!record.Add(textEditor.ToString())
{
}
}
I have an array consisting of following elements:
var schools = new [] {
new object[]{ new[]{ "1","2" }, "3","4" },
new object[]{ new[]{ "5","6" }, "7","8" },
new object[]{ new[]{ "9","10","11" }, "12","13" }
};
The real object that i try to flatten is from importing data into array of arrays from CSV and then joining it on values of fields:
var q =
from c in list
join p in vocatives on c.Line[name1].ToUpper() equals p.first_name.ToUpper() into ps
from p in ps.DefaultIfEmpty()
select new object[] { c.Line, p == null ? "(No vocative)" : p.vocative, p == null ? "(No sex)" : p.sex };
I want to flatten that array of strings to get:
string[] {
new string[]{ "1","2","3","4" },
new string[]{ "5","6","7","8" },
new string[]{ "9","10","11","12","13" }
}
I already have an solution that does that in loop, its not so performance-wise, but it seems to work ok.
I've tried to use SelectMany but cannot make up a solution.
Thank you very much for feedback ;)
I've tried answer from npo:
var result = schools.Select(z => z.SelectMany(y=> y.GetType().IsArray
? (object[])y : new object[] { y })
);
But CSVwriter class method accepts only explicitly typed:
IEnumerable<string[]>
So how to do it in linq, I've tried to:
List<string[]> listOflists = (List<string[]>)result;
But no go, InvalidCastException arrises, unfortunately.
In a first step, you have to normalize the data to one kind of type. Then you can iterate over them as you like. So at first create a method to flatten the values from a specific point to an arbitrary depth:
public static class Extensions
{
public static IEnumerable<object> FlattenArrays(this IEnumerable source)
{
foreach (var item in source)
{
if (item is IEnumerable inner
&& !(item is string))
{
foreach (var innerItem in inner.FlattenArrays())
{
yield return innerItem;
}
}
yield return item;
}
}
}
Now you can either iterate on the top level to get a single array of all values:
// Produces one array => ["1", "2", "3", "4", ...]
var allFlat = schools.FlattenArrays().OfType<string>().ToArray();
Or you can create individual array one depth deeper:
foreach (var item in schools)
{
// Produces an array for each top level e.g. ["5", "6", "7", "8"]
var flat = item.FlattenArrays().OfType<string>().ToArray();
}
As per the comments, because your inner array mixes elements of string[] and string, it likely won't be trivial to do this directly in Linq.
However, with the assistance of a helper function (I've called Flattener) you can branch the handling of both of the inner types manually to either return the elements in the array (if it's string[]), or to return the single element as an enumerable, if it's not. SelectMany can then be used to flatten the inner level, but the outer level seemingly you want to leave unflattened:
i.e.
var schools = new [] {
new object[]{new[]{"1","2"}, "3","4"},
new object[]{new[]{"5","6"}, "7","8"},
new object[]{new[]{"9","10","11"}, "12","13"}
};
var result = schools
.Select(s => s.SelectMany(o => Flattener(o)));
Which returns a type of IEnumerable<IEnumerable<string>>
Where the messy unpacking bit done by:
public IEnumerable<string> Flattener(object o)
{
if (o is IEnumerable<string> strings)
{
return strings;
}
if (o is string s)
{
return new[]{s};
}
return new[]{"?"};
}
Note the above uses the pattern matching capabilities of C#7.
Result screenshot courtesy of LinqPad:
If you want o do it via linq here is a sample
var schools = new[] {
new object[]{new[]{"1","2"}, "3","4"},
new object[]{new[]{"5","6"}, "7","8"},
new object[]{new[]{"9","10","11"}, "12","13"}
};
var result = schools.Select(z => z.SelectMany(y=> y.GetType().IsArray ? (object[])y : new object[] { y }));
The presented solution is devoted to convert any sort of int array, regular, jagged, or nested (these last are taken from javascript and its object notation, but they can also be implemented as complex jagged array of objects in C#), into a simple mono-dimensional integers array.
To adapt your request to it, you should have only change the string type elements of your objects jagged array into int type.
Here it is the C# function:
public static int[] getFlattenedIntArray(object jaggedArray)
{
var flattenedArray = new List<int>();
var jaggedArrayType = jaggedArray.GetType();
var expectedType = typeof(int);
if (jaggedArrayType.IsArray)
{
if (expectedType.IsAssignableFrom(jaggedArrayType.GetElementType()))
{
foreach (var el in jaggedArray as int[])
{
flattenedArray.Add(el);
}
}
else
{
foreach (var el in jaggedArray as object[])
{
foreach (var retIntEl in getFlattenedIntArray(el))
{
flattenedArray.Add(retIntEl);
}
}
}
}
else if (jaggedArrayType == expectedType)
{
flattenedArray.Add((int)jaggedArray);
}
else
{
return new int[0];
}
return flattenedArray.ToArray();
}
Try it in this fiddle: https://dotnetfiddle.net/5HGX96
I used ReSharper to inspect the code issues in my project and it notified me that the following loop could be converted into a LINQ-expression:
var dictionary = new Dictionary<string, string[]>
{
{ "400", new[] { "12345", "54321", "51423" } },
{ "500", new[] { "67890", "09876", "63727" } },
{ "600", new[] { "41713", "98234", "96547" } },
{ "700", new[] { "00000", "67990", "83752" } }
};
// ...
var targetValue = "41713";
foreach (string group in dictionary.Keys)
{
foreach (string name in dictionary[group])
{
if (name == targetValue)
return group;
}
}
return "User";
The loop basically checks the dictionary's values (string arrays) to see if targetValue belongs to any of them and returns the key of that array if found inside.
I tried doing the following, but clearly it just returns the value inside if its value is equivalent to targetValue.
var r = dictionary
.SelectMany(t => t.Value)
.FirstOrDefault(t => t == targetValue);
So you want to get the first key in the dictionary which string[]-value contains a given value?
var pairs = dictionary.Where(kv => kv.Value.Contains(myValue));
if (pairs.Any())
{
string group = pairs.First().Key;
}
or less readable but a little bit more efficient since it executes the query only once:
var pair = dictionary.FirstOrDefault(kv => kv.Value.Contains(myValue));
if (!pair.Equals(default(KeyValuePair<string, string[]>)))
{
string group = pair.Key;
}
last but not least another approach which is my favorite and also uses the "User"-default:
string group = dictionary.Where(kv => kv.Value.Contains(myValue))
.Select(kv=> kv.Key)
.DefaultIfEmpty("User")
.First();
var r = dictionary.FirstOrDefault(
x => x.Value.FirstOrDefault(y => y == myValue) != null);
This will also get the desired value back or null if it does not exist:
EDIT:
var result = dictionary.SkipWhile(n => !n.Value.Contains(myValue)).FirstOrDefault().Key;
//another way to get the key
//var result = dictionary.SingleOrDefault(n => n.Value.Contains(myValue)).Key;
if (result != null)
{
//do whatever with the result variable here
}
I have a list contained within a dictionary that contains the follwing values
value a
value b
value c
value 1, value 2, value3
value d
the end result I would like is
value a
value b
value c
value 1
value 2
value 3
value d
problem is iterating through the dictionary and trying to change the collection will not work as I am trying to modify it while looping through it
string[] ar;
foreach (var kvp in dict)
{
if (kvp.Key == "STAR-016")
{
foreach (var v in kvp.Value)
{
if (v.Contains(','))
{
ar = v.Split(',');
foreach (var a in ar)
{
kvp.Value.Add(a);
}
}
}
}
}
how can i get the desired result?
Try using LINQ:
var list = new List<string>()
{
"value a",
"value b",
"value 1, value 2, value 3",
"value c"
};
/* THIS IS THE IMPORTANT PART: */
/* Replace list with kvp.Value */
list = list.SelectMany(
i => i.Split( ',' ).Select( v => v.Trim() )
).ToList();
foreach ( var item in list )
Console.WriteLine( item );
Output:
value a
value b
value 1
value 2
value 3
value c
To use this within your code:
foreach (var kvp in dict)
{
if (kvp.Key == "STAR-016")
{
var newList =
kvp.Value.SelectMany(
i => i.Split( ',' ).Select( v => v.Trim() )
);
kvp.Value.Clear();
kvp.Value.AddRange(newList);
}
}
Thanks to #Mudu for pointing out the simpler i => syntax
Assuming that this is just an example and you actually want this not only for a key STAR-016 but for all where the value (which is a List<string>) contains a comma:
Dictionary<string, List<String>> dict = new Dictionary<string, List<String>>();
dict.Add("STAR-016", new List<string>() {
"value a", "value b", "value c", "value 1, value 2, value 3", "value d"
});
foreach (var kvp in dict)
{
for (int i = kvp.Value.Count - 1; i >= 0; i--)
{
string str = kvp.Value[i];
if (str.Contains(','))
{
var parts = str.Split(',').Select(p => p.Trim());
kvp.Value.RemoveAt(i);
kvp.Value.InsertRange(i, parts);
}
}
}
Demo
I'm looping from the end to the start to avoid complications because InsertRange will add new strings which increases the Count.
I'm using RemoveAt to replace the strings with commas with new strings(one for each part splitted by comma) which are added. I'm using InsertRange instead of AddRange because you want to keep the order.
Result:
value a
value b
value c
value 1
value 2
value 3
value d
You should be able to do that with one line using SelectMany.
dict["STAR-016"] = dict["STAR-016"].SelectMany(s=>s.Split(',')).ToList();
This replaces the list for your key with one that splits the string on comma, if the string doesn't contain a comma it just returns the string. You might also consider using StringSplitOptions.RemoveEmptyEntries if you don't want empty strings resulting from consecutive commas.
You can loop trough the List using for instead of foreach and modify items
for (int i = 0; i < kvp.Value.Count; i++)
{
if (kvp.Value[i].Contains(','))
...
}
(edit: juharr's https://stackoverflow.com/a/14875503/17713 does basically the same with build-in LINQ features and in more functional style, which is often more expressive in describing the actual problem)
I'd go for an approach with yield that does not modify the original collection. Here is an example code that operates on a List<string> orig which may also be in a dictionary:
public static void Main()
{
List<string> orig = new List<string>()
{
"value a",
"value b",
"value c",
"value 1, value 2, value 3",
"value d"
};
var result = Flatten(orig).ToList();
foreach(string s in result)
{
Console.WriteLine(s);
}
}
private static IEnumerable<string> Flatten(IList<string> orig)
{
foreach(string s in orig)
{
// split anyway, if there's no colon you just get a one-element
// array containing s, see
// http://msdn.microsoft.com/en-us/library/b873y76a.aspx
foreach(string v in s.Split(','))
{
yield return v.Trim();
}
}
}
In a dictionary, you could then replace the result with the original value:
dict["STAR-016"] = Flatten(dict["STAR-016"]).ToList()
Side note: The above code finds STAR-016 directly rather than using a foreach which is slower. If you did not shorten the code and you're actually just looking up STAR-016 I'd recommend you to use this way of dictionary lookup.
I know c# has Array.FindAll and Array.IndexOf.
Is there a Array.FindAllIndexOf which returns int[]?
string[] myarr = new string[] {"s", "f", "s"};
int[] v = myarr.Select((b,i) => b == "s" ? i : -1).Where(i => i != -1).ToArray();
This will return 0, 2
If the value does not exist in the array then it will return a int[0].
make an extension method of it
public static class EM
{
public static int[] FindAllIndexof<T>(this IEnumerable<T> values, T val)
{
return values.Select((b,i) => object.Equals(b, val) ? i : -1).Where(i => i != -1).ToArray();
}
}
and call it like
string[] myarr = new string[] {"s", "f", "s"};
int[] v = myarr.FindAllIndexof("s");
You can write something like :
string[] someItems = { "cat", "dog", "purple elephant", "unicorn" };
var selectedItems = someItems.Select((item, index) => new{
ItemName = item,
Position = index});
or
var Items = someItems.Select((item, index) => new{
ItemName = item,
Position = index}).Where(i => i.ItemName == "purple elephant");
Read : Get the index of a given item using LINQ
Searches for an element that matches the conditions defined by the specified predicate, and returns all the zero-based index of the occurrence within the entire System.Array.
public static int[] FindAllIndex<T>(this T[] array, Predicate<T> match)
{
return array.Select((value, index) => match(value) ? index : -1)
.Where(index => index != -1).ToArray();
}
I know this is an old post, but you can try the following,
string[] cars = {"Volvo", "BMW", "Volvo", "Mazda","BMW","BMW"};
var res = Enumerable.Range(0, cars.Length).Where(i => cars[i] == "BMW").ToList();
returns {1,4,5} as a list
No, there is not. But you can write your own extension method.
public static int[] FindAllIndexOf<T>(this T[] a, Predicate<T> match)
{
T[] subArray = Array.FindAll<T>(a, match);
return (from T item in subArray select Array.IndexOf(a, item)).ToArray();
}
and then, for your array, call it.
You can loop with findIndex giving an index
string[] arr = { "abc", "asd", "def", "abc", "lmn", "wer" };
int index = -1;
do
{
index = Array.IndexOf(arr, "abc", index + 1);
System.Console.WriteLine(index);
} while (-1 != index);
I've used Nikhil Agrawal's answer to create the following related method, which may be useful.
public static List<int> FindAllIndexOf<T>(List<T> values, List<T> matches)
{
// Initialize list
List<int> index = new List<int>();
// For each value in matches get the index and add to the list with indexes
foreach (var match in matches)
{
// Find matches
index.AddRange(values.Select((b, i) => Equals(b, match) ? i : -1).Where(i => i != -1).ToList());
}
return index;
}
Which takes a list with values and a list with values that are to be matched. It returns a list of integers with the index of the matches.
You can solve this problem by creating only 2 integer variables. More power to you!
string[] seasons= { "Fall","Spring", "Summer", "Fall", "Fall", "Winter"};
int i = 0;
int IndexOfFallInArray = 0;
int[] IndexesOfFall= new int[seasons.Length];
foreach (var item in seasons)
{
if (item == "Fall")
{
IndexesOfFall[i] = IndexOfFallInArray;
i++;
}
IndexOfFallInArray++;
}
How about simply:
public static IEnumerable<int> Available()
{
for (int i = 0; i < myarr.Length; i++)
{
if (myarr[i] is null) //Your predicate here...
yield return i;
}
}
I'm aware that the question is answered already, this is just another way of doing it. note that I used ArrayList instead of int[]
// required using directives
using System;
using System.Collections;
String inputString = "The lazy fox couldn't jump, poor fox!";
ArrayList locations = new ArrayList(); // array for found indexes
string[] lineArray = inputString.Split(' '); // inputString to array of strings separated by spaces
int tempInt = 0;
foreach (string element in lineArray)
{
if (element == "fox")
{
locations.Add(tempInt); // tempInt will be the index of current found index and added to Arraylist for further processing
}
tempInt++;
}