Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I'll explain my problem with an example: I want to sort the list(0,1,2,6,7) to the list(6,7,0,1,2), so every object in the list, that is say greater than 4 should be sorted in ascending order, beginning with the smallest and then everything that is smaller than 4, in ascending order, beginning with the smallest.
Make this comparer:
public class MyComparer : IComparer<int>
{
public int Divider { get; set; }
public MyComparer(int divider) { Divider = divider; }
public int Compare(int x, int y)
{
if (x < Divider && y > Divider) return 1;
if (x > Divider && y < Divider) return -1;
return x.CompareTo(y);
}
}
And then you can run this code:
List<int> list = new List<int> { 0, 1, 2, 6, 7 };
list = list.OrderBy(i => i, new MyComparer(4)).ToList();
Note it's not clear in the question how you want to handle the value 4 itself, if it should be first entry in the 6,7 group or the last entry in the 0,1,2 group. Based on what you expect to happen, one of the inequality comparison on each if() line above should include an =.
Doably with linq:
using System;
using System.Collections.Generic;
using System.Linq;
internal class Program
{
static void Main(string[] args)
{
List<int> myList = new List<int> { 0, 1, 2, 6, 7 };
var bigger4 = myList // from myList
.Where(item => item > 4) // filter all bigger 4
.OrderBy(i => i) // order them by value ascending
.ToList(); // make list to allow AddRange() later
// I am putting 4rs into this list
var smaller5 = myList // from myList
.Where(item => item <= 4) // filter all smalller equal 4
.OrderBy(i => i); // order them by value ascending
bigger4.AddRange(smaller5); // add to first list
// output as string with , between values:
Console.WriteLine(string.Join(",", bigger4)); // which smaller5 added into bigger4
Console.ReadLine();
}
}
Or put it in a Extensionmethod:
internal static class SillyThing
{
static public IList<int>
SortBigger_K_InFrontAnscendingThenAddSmallerEqual_K_Ascending(
this IList<int> list, int K)
{
var biggerK = list
.Where(item => item > K) // filter all bigger K
.OrderBy(i => i) // order them by value ascending
.ToList(); // make list to allow AddRange() later
// I am putting 4rs into this list
var smallerK = list
.Where(item => item <= K) // filter all smalller equal K
.OrderBy(i => i); // order them by value ascending
biggerK.AddRange(smallerK); // add to first list
return biggerK;
}
}
And call it like so:
var newList = "1,2,3,4,5,6,7,8,9,0,12,14,16,17,192,222,66,44,22,11".Split(',')
.Select(n => int.Parse(n))
.ToList();
Console.WriteLine(
string.Join(",", newList.
SortBigger_K_InFrontAnscendingThenAddSmallerEqual_K_Ascending(70)));
Console.ReadLine();
Disclaimer:
#JoelCoehoorn 's method is smater for reusability then using an extensionmethod that is that specific and SILLY.
Related
How to check duplicate string array in list?
I declare string array list like this:
List<string[]> list = new List<string[]>();
and I add a few items in the list.
list.Add(new string[3] {"1","2","3"});
list.Add(new string[3] {"2","3","4"});
list.Add(new string[1] {"3"});
list.Add(new string[1] {"3"});
list.Add(new string[3] {"1","2","3"});
now I want to get to know which items are duplicated. I tried like below to add the duplicated items to new list:
for (int j = 0; j < list.Count - 1; j++)
{
for (int k = list.Count - 1; k > j; k--)
{
if (j != k)
{
if (Enumerable.SequenceEqual(list[j], list[k]))
{
savedDistinctList.Add(list[j]);
}
}
}
}
and finally I want to remove the duplicated item in the first list. so I want to see 3 items in the list.([1,2,3],[2,3,4],[3])
Perhaps any idea using LINQ or something else?
First we have to teach .Net how to compare arrays:
private sealed class ArrayEqualityComparer<T> : IEqualityComparer<T[]> {
public bool Equals(T[] left, T[] right) {
if (ReferenceEquals(left, right))
return true;
if (left is null || right is null)
return false;
return left.SequenceEqual(right);
}
public int GetHashCode(T[] array) => array is null
? -1
: array.Length;
}
Then you can use Linq Distinct with this class implemented:
using System.Linq;
...
savedDistinctList = list
.Distinct(new ArrayEqualityComparer<string>())
.ToList();
If you want to modify the existing list, you can use HashSet<T>:
var unique = new HashSet<string[]>(new ArrayEqualityComparer<string>());
for (int i = list.Count - 1; i >= 0; --i)
if (!unique.Add(list[i]))
list.RemoveAt(i);
This has already been replied here: C# LINQ find duplicates in List by #Save
The easiest way to solve the problem is to group the elements based on their value, and then pick a representative of the group if there are more than one element in the group. In LINQ, this translates to:
var query = lst.GroupBy(x => x)
.Where(g => g.Count() > 1)
.Select(y => y.Key)
.ToList();
If you want to know how many times the elements are repeated, you can use:
var query = lst.GroupBy(x => x)
.Where(g => g.Count() > 1)
.Select(y => new { Element = y.Key, Counter = y.Count() })
.ToList();
This will return a List of an anonymous type, and each element will have the properties Element and Counter, to retrieve the information you need.
And lastly, if it's a dictionary you are looking for, you can use
var query = lst.GroupBy(x => x)
.Where(g => g.Count() > 1)
.ToDictionary(x => x.Key, y => y.Count());
This will return a dictionary, with your element as key, and the number of times it's repeated as value.
Apply with a foreach on your list.
I have a list with four double values in it
var numbers2 = new List<double>() { 2, 3, 9, 7 };
I need to get lower value between the first 2 indexes (2 and 3).
Similarly I need to get lower value between index 3 and 4 (9 and 7)
Is there a way in C sharp to determine this using LINQ?
Once I have the lower value from above list i.e 2 and 7; I need to pass these values in the below loop
for (int i = 0; i < 1; i++)
{
dac[i] = SetValue(lowerValue[j]);
}
if i == 0, I want lowerValue[j] = 2. If i == 1, I want lowerValue[j] = 7
Well as others have pointed out, it doesn't seem like there's any reason to use linq. But if you absolutely had to find some way to do it, then it's possible. I'll throw 3 options out, the last one being linq.
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var numbers2 = new List<double>() { 2, 3, 9, 7 };
// you stated it's always 4 values. There's no reason to use linq. The optimal solution would
// be a variation of this (with some constant values instead of magic numbers)..
var first = Math.Min(numbers2[0],numbers2[1]);
var second = Math.Min(numbers2[2],numbers2[3]);
Console.WriteLine($"Lower values: {first},{second}");
// if it was an arbitry sized list (but always even count) you could use a iterator method
var listOfLowerValues = ToPairs(numbers2);
var values = string.Join(",", listOfLowerValues.Select(x => x.ToString()));
Console.WriteLine($"Lower values: {values}");
// finally if you absolutely had too, you can make it even more inefficient
// by using linq.
var indexes = Enumerable.Range(0, numbers2.Count);
var indexed = numbers2.Zip(indexes, (n,i) => (index: i, num: n));
var odd = indexed.Where(x => x.index%2 == 0).Select(x => x.num).ToArray();
var even = indexed.Where(x => x.index%2 > 0).Select(x => x.num).ToArray();
var lower = even.Zip(odd,(v1,v2)=> v1 < v2 ? v1 : v2);
var valuesByLinq = string.Join(",",lower.Select(x => x.ToString()));
Console.WriteLine($"Lower values: {valuesByLinq}");
}
static IEnumerable<double> ToPairs(IEnumerable<double> source)
{
int index = 0;
double previous = 0;
foreach(var n in source)
{
if(index++%2 > 0)
{
yield return (previous < n) ? previous : n;
}
else
{
previous = n;
}
}
}
}
This question already has answers here:
The Most frequent Number in an array
(11 answers)
How do I find duplicates in an array and display how many times they occurred?
(14 answers)
Closed 1 year ago.
I'm trying to know the number that is most present in one array.
Example:
8 4 3 8 4 4 1 5
In this case, I want the program to tell me the number is 4.
This is what I've written so far, but the console.Writeline returns an error system.32.
Can you help ?
int[] moda = new int[21];
for (int j = 0; j < avaliacoes.Length; j++)
{
int avaliacao = avaliacoes[j];
moda[avaliacao] = moda[avaliacao] + 1;
}
Console.WriteLine("\nA moda é{0}: ", moda);
Grouping with some ordering on group size should get the job done.
To summarize what I am doing I am grouping the array by there values, I then Emit that group into an anonymous type that will hold the key as well as the group count. Third I order by descending so we order or enumerable from high value to low, which will allow me to say the first element in the enumeration must be the top value (if not equal to the top value).
class Program
{
static void Main(string[] args)
{
var a = new[] { 8, 4, 3, 8, 4, 4, 1, 5 };
var mostPresent = a
.GroupBy(e => e)
.Select(e => new { Key = e.Key, Count = e.Count() })
.OrderByDescending(e => e.Count)
.FirstOrDefault();
Console.WriteLine(mostPresent);
}
}
There are many methods for doing that, here is one:
static void Main(string[] args)
{
// Create array
const int arrLength = 10;
int[] mainArray = new int[arrLength];
// Fill array with random numbers
Random randNum = new Random();
for (int i = 0; i < mainArray.Length; i++)
{
mainArray[i] = randNum.Next(10);
}
// Create a dictionary for counting
Dictionary<int, int> countDic = new Dictionary<int, int>();
foreach (var currentNumber in mainArray)
{
// If the current number has appeared before increase the count for that number
if (countDic.TryGetValue(currentNumber, out int _))
{
countDic[currentNumber]++;
}
// If it's first time current number has appeared set its count as one
else
{
countDic.Add(currentNumber, 1);
}
}
// Print frequency of numbers
foreach (var num in countDic)
{
Console.WriteLine($"{num.Key} appears {num.Value} times in the array!");
}
// Print the number which appears the most in the array
int maxNum = countDic.Aggregate((x, y) => x.Value > y.Value ? x : y).Key;
Console.WriteLine(maxNum);
Console.Read();
}
Ask me if there is anything that you can not understand in the solution
You can do that easily with LINQ. I'll explain what happens below:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
List<int> ints = new List<int> { 8, 4, 3, 8, 4, 4, 1, 5 };
var query = from i1 in ints
select new
{
number = i1,
count = ints.Where(i2 => i2 == i1).Count()
};
var most = query.OrderByDescending(x => x.count).First().number;
Console.WriteLine($"Number: {most}");
Console.ReadKey();
}
}
}
In the first query I iterate over all ints in the list and create an anonymous object with 2 properties 'number' and 'count'. The count contains the amount of ints in the list with the number of the first iteration.
In the second line the 'most'is selected by ordering the anonymous types in descending order on 'count' and taking the First item of that result. If you have more numbers in your array with the same number of entries you will get only the first one. So if '4' and 5' both are present 4 times it is not guaranteed which result you will get. It could be '4' but just as well '5'.
I'm working on aforge. I have a list of data which I Draw on the screen next to blobs. I'm also adding the data to a list box. But Instead of getting added in a left to right sequence, its getting added as per the blob's XY coordinate as shown below in the first listbox.
I tried to sort the list using Linq by the OrderBy method but then it orders the whole list in ascending order. I dont want that, I want the list to be sorted by the first line, then the next line and so on. I tried using take<> to sort it by the first row, but it only sorts the first row of 5 int and then stops.
Code :
int n = 5;
elements = elements.Take(n).OrderBy(i => i).ToList();
foreach (var cogxx in elements)
{
listBox2.Items.Add(cogxx);
}
If List coord = new List{80,90,100,60,70,20,40,30,10,50,} if user input int row is 2 then output should be {60,70,80,90,100,10,20,30,40,50} How can I do this?
If you have no special class representing your Line object, then you can use regex to parse the string. In this case, I use name capture group of Regex:
List<string> elements = new List<string>
{
"Line 1 int 1",
"Line 2 int 1",
"Line 1 int 2",
"Line 1 int 3",
"Line 2 int 2",
"Line 2 int 12",
};
var pattern = #"^\bLine \b(?<num1>\d+) \bint \b(?<num2>\d+)$";
Regex regex = new Regex(pattern);
var query =
from e in elements
where regex.Match(e).Success
orderby
int.Parse(regex.Match(e).Groups["num1"].Value),
int.Parse(regex.Match(e).Groups["num2"].Value)
select e;
var orderedResult = query.ToList();
Or the same with fluent API LINQ:
var orderedResult =
elements
.Where(e => regex.Match(e).Success)
.OrderBy(e => int.Parse(regex.Match(e).Groups["num1"].Value))
.ThenBy(e => int.Parse(regex.Match(e).Groups["num2"].Value))
.ToList();
The orderedResult should be:
Line 1 int 1
Line 1 int 2
Line 1 int 3
Line 2 int 1
Line 2 int 2
Line 2 int 12
UPDATE:
Create a class and extension methods that would split your list into chunks:
public static class MyLinqExtensions
{
public static IEnumerable<IEnumerable<T>> Batch<T>(
this IEnumerable<T> source, int batchSize)
{
using (var enumerator = source.GetEnumerator())
while (enumerator.MoveNext())
yield return YieldBatchElements(enumerator, batchSize - 1);
}
private static IEnumerable<T> YieldBatchElements<T>(
IEnumerator<T> source, int batchSize)
{
yield return source.Current;
for (int i = 0; i < batchSize && source.MoveNext(); i++)
yield return source.Current;
}
}
This code was taken from this answer.
Then you use Batch extension method the following way:
List<int> coord = new List<int> { 80, 90, 100, 60, 70, 20, 40, 30, 10, 50 };
int n = 5;
var orderedResult =
coord.Batch(n)
.Select(b => b.OrderBy(i => i))
.SelectMany(x => x)
.ToList();
If you want to learn LINQ, LINQPad is your friend.
I've an object that is include property ID with values between 101 and 199. How to order it like 199,101,102 ... 198?
In result I want to put last item to first.
The desired ordering makes no sense (some reasoning would be helpful), but this should do the trick:
int maxID = items.Max(x => x.ID); // If you want the Last item instead of the one
// with the greatest ID, you can use
// items.Last().ID instead.
var strangelyOrderedItems = items
.OrderBy(x => x.ID == maxID ? 0 : 1)
.ThenBy(x => x.ID);
Depending whether you are interested in the largest item in the list, or the last item in the list:
internal sealed class Object : IComparable<Object>
{
private readonly int mID;
public int ID { get { return mID; } }
public Object(int pID) { mID = pID; }
public static implicit operator int(Object pObject) { return pObject.mID; }
public static implicit operator Object(int pInt) { return new Object(pInt); }
public int CompareTo(Object pOther) { return mID - pOther.mID; }
public override string ToString() { return string.Format("{0}", mID); }
}
List<Object> myList = new List<Object> { 1, 2, 6, 5, 4, 3 };
// the last item first
List<Object> last = new List<Object> { myList.Last() };
List<Object> lastFirst =
last.Concat(myList.Except(last).OrderBy(x => x)).ToList();
lastFirst.ForEach(Console.Write);
Console.WriteLine();
// outputs: 312456
// or
// the largest item first
List<Object> max = new List<Object> { myList.Max() };
List<Object> maxFirst =
max.Concat(myList.Except(max).OrderBy(x => x)).ToList();
maxFirst.ForEach(Console.Write);
Console.WriteLine();
// outputs: 612345
Edit: missed the part about you wanting the last item first. You could do it like this :
var objectList = new List<DataObject>();
var lastob = objectList.Last();
objectList.Remove(lastob);
var newList = new List<DataObject>();
newList.Add(lastob);
newList.AddRange(objectList.OrderBy(o => o.Id).ToList());
If you are talking about a normal sorting you could use linq's order by method like this :
objectList = objectList.OrderBy(ob => ob.ID).ToList();
In result I want to put last item to first
first sort the list
List<int> values = new List<int>{100, 56, 89..};
var result = values.OrderBy(x=>x);
add an extension method for swaping an elements in the List<T>
static void Swap<T>(this List<T> list, int index1, int index2)
{
T temp = list[index1];
list[index1] = list[index2];
list[index2] = temp;
}
after use it
result .Swap(0, result.Count -1);
You can acheive this using a single Linq statment.
var ordering = testData
.OrderByDescending(t => t.Id)
.Take(1)
.Union(testData.OrderBy(t => t.Id).Take(testData.Count() - 1));
Order it in reverse direction and take the top 1, then order it the "right way round" and take all but the last and union these together. There are quite a few variants of this approach, but the above should work.
This approach should work for arbitrary lists too, without the need to know the max number.
How about
var orderedItems = items.OrderBy(x => x.Id)
var orderedItemsLastFirst =
orderedItems.Reverse().Take(1).Concat(orderedItems.Skip(1));
This will iterate the list several times so perhaps could be more efficient but doesn't use much code.
If more speed is important you could write a specialised IEnumerable extension that would allow you to sort and return without converting to an intermediate IEnumerable.
var myList = new List<MyObject>();
//initialize the list
var ordered = myList.OrderBy(c => c.Id); //or use OrderByDescending if you want reverse order