Sort by numbers first - c#

I have a list as under
var x = new List<string>() { "a", "1", "b", "2" };
var sortedResultByNumeric = x
.Select(s => new { OriginalString = s,
ExtractNumbers = String.Join("", s.Where(Char.IsDigit)) })
.OrderBy(o => o.ExtractNumbers).ToList();
The output is
a
b
1
2
But expected is
1
2
a
b
How can I do so?

The output that you see and expected is based on OriginalString because ExtractNumbers is "","","1","2" then you should OrderBy OriginalString:
var x = new List<string>() { "a", "1", "b", "2" };
var sortedResultByNumeric = x
.Select(s => new { OriginalString = s, ExtractNumbers = String.Join("", s.Where(Char.IsDigit)) })
.OrderBy(o => o.OriginalString).ToList();
The output:
1
2
a
b

You can try this:
var myList = new List<string>() { "a", "1", "b", "2", "123", "cd", "12346", "657" };
var nonNumericItems = myList.Where(item => !item.Any(i => Char.IsDigit(i)))
.OrderBy(item => item);
var numericItems = myList
.Select(item => String.Join("", item.Where(Char.IsDigit)))
.Where(item => !String.IsNullOrEmpty(item))
.Select(item => Convert.ToInt32(item))
.OrderBy(item => item)
.Select(item => item.ToString());
var result = numericItems
.Union(nonNumericItems);
result.ToList()
.ForEach(Console.WriteLine);
The output is:
1, 2, 123, 657, 12346, a, b, cd
P.S: You didn't tell us any additional explanation about your logic here String.Join("", item.Where(Char.IsDigit). So, I didn't ask any additional question about that.

If your list contains only 1-char strings you can order them by their char numerical values:
var x = new List<string>() { "a", "1", "b", "2" };
var sorted = x.OrderBy(c => Convert.ToChar(c))
.ToList();
foreach (var c in sorted)
Console.WriteLine(c);
// 1
// 2
// a
// b

This solution may seem complicated but it will work with any Net built in sort method.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication63
{
class Program
{
const string FILENAME = #"c:\temp\test.csv";
static void Main(string[] args)
{
List<AlphaNumberic> aN = new List<AlphaNumberic>() { "a", "1", "b", "2" };
aN.Sort((x, y) => x.CompareTo(y));
}
}
public class AlphaNumberic
{
int results = 0;
public string alphaNumeric { get; set; }
public AlphaNumberic(string value)
{
this.alphaNumeric = value;
}
public static implicit operator string(AlphaNumberic d)
{
return d.alphaNumeric;
}
public static implicit operator AlphaNumberic(string d)
{
return new AlphaNumberic(d);
}
public int CompareTo(AlphaNumberic compareItem)
{
int thisNum;
int num;
if (int.TryParse(compareItem.alphaNumeric, out num))
{
if (int.TryParse(this.alphaNumeric, out thisNum))
{
//alphaNumeric and compareItem both integers;
results = thisNum.CompareTo(num);
}
else
{
//alphaNumeric not an integer and compareItem integers;
results = 1;
}
}
else
{
if (int.TryParse(this.alphaNumeric, out thisNum))
{
//alphaNumeric is an integer and compareItem not an integers;
results = -1;
}
else
{
//alphaNumeric not an integer and compareItem not an integers;
results = alphaNumeric.CompareTo(compareItem);
}
}
return results;
}
}
}

Related

Using linq to group/aggregate by string[]

Suppose I have a list with a Tuple.First as a string[] (i.e. my key):
var lst = new List<Tuple<string[], int>> {
new Tuple<string[], int>(new string[] { "A", "B" }, 5),
new Tuple<string[], int>(new string[] { "A", "B" }, 10),
new Tuple<string[], int>(new string[] { "C", "B" }, 10),
}
I would like to aggregate (e.g. Sum) by Tuple.First (i.e. string[]) so expecting an output as follows:
var output = new List<Tuple<string[], int>> {
new Tuple<string[], int>(new string[] { "A", "B" }, 15),
new Tuple<string[], int>(new string[] { "C", "B" }, 10),
}
I did it this way but there must be a cleaner way to do it instead of forcing a pipe concatenation:
var output = lst
.GroupBy(x => string.Join("|", x.First))
.Select(x => new Tuple<string[], int>(
x.Key.Split('|'),
Sum(x => x.Second)));
You need to implement IEqualityComparer for string[]. My example using touples (You don't say what Pair is):
static void Main(string[] args)
{
var lst = new List<(string[] arr, int num)> {
(new string[] { "A", "B" }, 5),
(new string[] { "A", "B" }, 10),
(new string[] { "C", "B" }, 10),
};
var grouped = lst.GroupBy(pair => pair.arr, new EnumerableComparer());
var sums = grouped.Select(g => (arr: g.Key, num: g.Sum(p => p.num)));
}
class EnumerableComparer<TRecord> : IEqualityComparer<IEnumerable<TRecord>>
{
public bool Equals(IEnumerable<TRecord> x, IEnumerable<TRecord> y)
{
return ReferenceEquals(x, y) || x != null && x.SequenceEqual(y);
}
public int GetHashCode(IEnumerable<TRecord> lst)
{
unchecked
{
int hash = 387;
foreach (var elem in lst)
{
hash = hash * 31 + elem.GetHashCode();
}
return hash;
}
}
}

Using Linq Select to create a list that contains more items than the original list

I have a list of items. For example (although the list could be any length):
var inputList = new List<Input1>()
{
new Input1() { Test = "a" },
new Input1() { Test = "b" }
};
What I want to do is create a new list of:
a1, a2, b8, b9
That is the value of Test (i.e. a) with a suffix based on the value of Test.
In that order. Obviously, this is a minimum workable example, not the actual problem. So I'd like to use something like the .Select to split the data - something like this:
var outputList = inputList.Select(x =>
{
if (x.Test == "a")
{
return new Input1() { Test = "a1" };
//return new Input1() { Test = "a2" };
}
else if (x.Test == "b")
{
return new Input1() { Test = "b8" };
//return new Input1() { Test = "b9" };
}
else
{
return x;
}
});
Input1 for completeness:
class Input1
{
public string Test { get; set; }
}
That is, to return a list that contains items that were not in the original list.
I realise I can use a foreach, but I'm interested if there's a better / more concise way.
Suppose you have a method that transforms your single input into multiple inputs:
public static Input1[] Transform(Input1 x)
{
if (x.Test == "a") return new[] {new Input1("a1"), new Input1("a2")};
if (x.Test == "b") return new[] {new Input1("b8"), new Input1("b9")};
return new[] {x};
}
(This is just from your toy example - I guess you actually need a transformation that is more meaningful.)
Then you can just use SelectMany to get your desired result in the correct order:
inputList
.SelectMany(Transform);
If you're using C# 8.0 or above, you may use switch expression as follows:
var outputList =
inputList.SelectMany(x => x.Test switch
{
"a" => new[] { new Input1() { Test = "a1" }, new Input1() { Test = "a2" } },
"b" => new[] { new Input1() { Test = "b8" }, new Input1() { Test = "b9" } },
_ => new[] { x }
})
.ToList();

Cross join of unknown number of string arrays using linq

Is it possible to do a cross join using linq where the number of joins is not known in advance?
I have this:
var arrays = new List<string[]>();
If I know I have three lists I can do:
var oQuery = from x in arrays[0]
from y in arrays[1]
from z in arrays[2]
select new {x, y, z};
Is it be possible to join n string arrays using linq?
Try this solution, where each item from result will not be look like {x:"A", y:"B", ... }, because number of properties is not predicted, so instead, it will be like ["A", "B", ... ]:
public static List<List<string>> CrossJoin(List<string[]> arrays)
{
var data = arrays.Select(x => x.ToList()).ToList();
List<List<string>> result = data[0].Select(x => new List<string> { x }).ToList();
for (var i = 1; i < data.Count; i++)
result = (from a in result
from b in data[i]
select new { a, b })
.Select(x => x.a.Concat(new List<string> { x.b }).ToList())
.ToList();
return result;
}
Usage:
var arr1 = new[] { "A", "B", "C" };
var arr2 = new[] { "D", "E" };
var arr3 = new[] { "F", "G" };
var result = CrossJoin(new List<string[]> { arr1, arr2, arr3 });
for(var i = 0; i < result.Count; i++)
Console.WriteLine(string.Format("{0}: {1}", i + 1, string.Join(",", result[i])));
Please try below solution to get Cartesian product for given "N" number of string arrays.
static void Main(string[] args)
{
var arrays = new List<string[]>();
var arr1 = new string[] { "A", "B", "C" };
var arr2 = new string[] { "D", "E", "F" };
var arr3 = new string[] { "G", "H", "I" };
arrays.Add(arr1);
arrays.Add(arr2);
arrays.Add(arr3);
IEnumerable<Tuple<string,string>> oQuery1 = null;
int count = arrays.Count;
var k = arrays[0].AsEnumerable();
for (int i =1; i< count; i++)
{
var l1 = arrays[i];
oQuery1 = k.SelectMany((x) => l1, (x, y) => Tuple.Create( x, y ));
k = oQuery1.Select(x=>x.ToString());
}
}
[Fact]
public void Test1()
{
var listOfStringList = new List<List<string>>();
var list1 = new List<string> { "A", "B", "C" };
var list2 = new List<string> { "AA", "BB", "CC" };
var list3 = new List<string> { "AAA", "BBB", "CCC" };
listOfStringList.Add(list1);
listOfStringList.Add(list2);
listOfStringList.Add(list3);
var resultData = new List<List<string>>();
listOfStringList.ForEach((stringList) =>
{
if (resultData.Count == 0)
{
resultData = stringList.Select(u => new List<string> { u }).ToList();
return;
}
resultData = resultData.SelectMany(u => stringList
.Select(v =>
{
var list = new List<string>();
u.ForEach(sv =>
{
list.Add(sv);
});
list.Add(v);
return list;
}).ToList()).ToList();
});
// Ignore variable resultantData it is just for highlight
var resultantData = resultData;
}

Get the element with in the array whose occurrence is 4 times

I have an array of strings , i want to find those element whose occurrence are 4 or more than 4 times with in the array.
my code
internal static void ProcessArray(string[] numArray)
{
string response = string.Empty;
var duplicates = (numArray
.GroupBy(e => e)
.Where(e => e.Count() >= 4)
.Select(e => e.First())).ToList();
//do some further business logic
}
So duplicate should return me a list of string which has the element.
I am calling this from my method below
Public static string GetDuplicates()
{
string[] s = new new string[]{" 1","1","2","2","2","1","3,"2","1" }
string result = ProcessArray(s);
return result
}
it only returns 2 in the list , the correct result should be 1,2 in the list.
var values = new string [] { "1", "1", "2", "2", "2", "1", "3", "2", "1" };
var groups = values.GroupBy(i => i).Select(i => new { Number = i.Key, Count = i.Count() });
foreach(var item in groups)
{
if(item.Count == 4)
{
Console.WriteLine(item.Number);
}
}
WORKING FIDDLE

Generate all Combinations from Multiple (n) Lists

EDIT: I am completely redoing my questions as I have figured out the simplest way of asking it. Thanks to the commenters so far that got me thinking about the root problem.
public List<string> GetAllPossibleCombos(List<List<string>> strings)
{
List<string> PossibleCombos = new List<string>();
//????
{
string combo = string.Empty;
// ????
{
combo += ????
}
PossibleCombos.Add(combo);
}
return PossibleCombos;
}
I need to figure out how to recursively go through each List<string> and combine 1 string from each list into a combo string. Don't worry too much about formatting the string as the "live" code uses a custom object instead. Also, feel free to assume that every list will contain at least 1 string and that there are no null values.
Here is a simple non-recursive solution that just concatenates the elements of each combination:
public static List<string> GetAllPossibleCombos(List<List<string>> strings)
{
IEnumerable<string> combos = new [] { "" };
foreach (var inner in strings)
combos = from c in combos
from i in inner
select c + i;
return combos.ToList();
}
static void Main(string[] args)
{
var x = GetAllPossibleCombos(
new List<List<string>>{
new List<string> { "a", "b", "c" },
new List<string> { "x", "y" },
new List<string> { "1", "2", "3", "4" }});
}
You could generalize this to return an IEnumerable<IEnumerable<string>>, which allows the caller to apply any operation they like for transforming each combination into a string (such as the string.Join below). The combinations are enumerated using deferred execution.
public static IEnumerable<IEnumerable<string>> GetAllPossibleCombos(
IEnumerable<IEnumerable<string>> strings)
{
IEnumerable<IEnumerable<string>> combos = new string[][] { new string[0] };
foreach (var inner in strings)
combos = from c in combos
from i in inner
select c.Append(i);
return combos;
}
public static IEnumerable<TSource> Append<TSource>(
this IEnumerable<TSource> source, TSource item)
{
foreach (TSource element in source)
yield return element;
yield return item;
}
static void Main(string[] args)
{
var combos = GetAllPossibleCombos(
new List<List<string>>{
new List<string> { "a", "b", "c" },
new List<string> { "x", "y" },
new List<string> { "1", "2", "3", "4" }});
var result = combos.Select(c => string.Join(",", c)).ToList();
}
Hope this helps.
class NListBuilder
{
Dictionary<int, List<string>> tags = new Dictionary<int, List<string>>();
public NListBuilder()
{
tags.Add(1, new List<string>() { "A", "B", "C" });
tags.Add(2, new List<string>() { "+", "-", "*" });
tags.Add(3, new List<string>() { "1", "2", "3" });
}
public List<string> AllCombos
{
get
{
return GetCombos(tags);
}
}
List<string> GetCombos(IEnumerable<KeyValuePair<int, List<string>>> remainingTags)
{
if (remainingTags.Count() == 1)
{
return remainingTags.First().Value;
}
else
{
var current = remainingTags.First();
List<string> outputs = new List<string>();
List<string> combos = GetCombos(remainingTags.Where(tag => tag.Key != current.Key));
foreach (var tagPart in current.Value)
{
foreach (var combo in combos)
{
outputs.Add(tagPart + combo);
}
}
return outputs;
}
}
}
In case it helps anyone, here is the method syntax version for Douglas's GetAllPossibleCombos method.
public static List<string> GetAllPossibleCombos(List<List<string>> strings)
{
IEnumerable<string> combos = new[] { "" };
foreach (var inner in strings)
{
combos = combos.SelectMany(r => inner.Select(x => r + x));
}
return combos.ToList();
}
Here is a generic version that works with all object types:
public static List<List<T>> GetAllPossibleCombos<T>(List<List<T>> objects)
{
IEnumerable<List<T>> combos = new List<List<T>>() { new List<T>() };
foreach (var inner in objects)
{
combos = combos.SelectMany(r => inner
.Select(x => {
var n = r.DeepClone();
if (x != null)
{
n.Add(x);
}
return n;
}).ToList());
}
// Remove combinations were all items are empty
return combos.Where(c => c.Count > 0).ToList();
}
If you provide null values it will also give you empty combination. For example:
var list1 = new List<string>() { "1A", "1B", null };
var list2 = new List<string>() { "2A", "2B", null };
var output = GetAllPossibleCombos(allLists);
Will contain:
[["1A"], ["1B"], ["2A"], ["2B"], ["1A", "2A"], ["1A", "2B"], ["1B", "2A"], ["1B," "2B"]]
Rather than just:
var list1 = new List<string>() { "1A", "1B" };
var list2 = new List<string>() { "2A", "2B" };
var output = GetAllPossibleCombos(allLists);
[["1A", "2A"], ["1A", "2B"], ["1B", "2A"], ["1B," "2B"]]
Note: DeepClone is an extension method used for copying the list. This can be done in many ways
public static T DeepClone<T>(this T source)
{
// Don't serialize a null object, simply return the default for that object
if (Object.ReferenceEquals(source, null))
{
return default(T);
}
var deserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source), deserializeSettings);
}
Here is an answer that would work for any generic type, comes with a function to convert base-10 to base-n as well.
public static class IntExt
{
const string Symbols = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
public static string ToString(this int value, int toBase)
{
switch (toBase)
{
case 2:
case 8:
case 10:
case 16:
return Convert.ToString(value, toBase);
case 64:
return Convert.ToBase64String(BitConverter.GetBytes(value));
default:
if (toBase < 2 || toBase > Symbols.Length)
throw new ArgumentOutOfRangeException(nameof(toBase));
if (value < 0)
throw new ArgumentOutOfRangeException(nameof(value));
int resultLength = 1 + (int)Math.Max(Math.Log(value, toBase), 0);
char[] results = new char[resultLength];
int num = value;
int index = resultLength - 1;
do
{
results[index--] = Symbols[num % toBase];
num /= toBase;
}
while (num != 0);
return new string(results);
}
}
}
public class UnitTest1
{
public static T[][] GetJoinCombinations<T>(T[][] arrs)
{
int maxLength = 0;
int total = 1;
for (int i = 0; i < arrs.Length; i++)
{
T[] arr = arrs[i];
maxLength = Math.Max(maxLength, arr.Length);
total *= arr.Length;
}
T[][] results = new T[total][];
int n = 0;
int count = (int)Math.Pow(maxLength, arrs.Length);
for (int i = 0; i < count; i++)
{
T[] combo = new T[arrs.Length];
string indices = i.ToString(maxLength).PadLeft(arrs.Length, '0');
bool skip = false;
for (int j = 0; j < indices.Length; j++)
{
T[] arr = arrs[j];
int index = int.Parse(indices[j].ToString());
if (index >= arr.Length)
{
skip = true;
break;
}
combo[j] = arr[index];
}
if (!skip)
results[n++] = combo;
}
return results;
}
[Fact]
public void Test1()
{
string[][] results = GetJoinCombinations(new string[][]
{
new string[] { "1", "2", "3" },
new string[] { "A", "B", "C" },
new string[] { "+", "-", "*", "/" },
});
}
}
public static IEnumerable<int[]> GetAllPossibleCombos(List<int[]> ints)
=> _getAllPossibleCombos(ints, 0, new List<int>());
private static IEnumerable<int[]> _getAllPossibleCombos(IReadOnlyList<int[]> ints, int index, IReadOnlyCollection<int> current)
{
return index == ints.Count - 1
? ints[index]
.Select(_ => new List<int>(current) {_}.ToArray())
: ints[index]
.SelectMany(_ => _getAllPossibleCombos(ints, index + 1, new List<int>(current) {_}));
}

Categories

Resources