Insert duplicates values linq - c#

In a collection of values
[0, 2, 25, 30]
I'm trying to do with linq
[0, 0, 0, 2, 2, 2, 25, 25, 25, 30, 30, 30] //Replicate 2 times (values repeated 3 times)
Is there someway to do it with linq?

With value types it's easy, just use Enumerable.Repeat:
var result = collection.SelectMany(x => Enumerable.Repeat(x, 3));
If it was an array use ToArray if it was a list use ToList at the end.
With reference types it depends if you really want the same reference, then you can also use Repeat. Otherwise you need to create "deep-clones" of the instance, for example by using a copy constructor if available:
var result = collection
.SelectMany(x => Enumerable.Range(1, 3).Select(i => new YourType(x)));

Of course Tim's answer does answer this question. But posting this as an alternative answer (if you have to repeat for fewer times)
List<int> list = new List<int>() { 0, 1, 2, 3 };
List<int> newList = list.SelectMany(x => new List<int>(3) { x, x, x }).ToList();

Related

Efficient way to get index to sorted distinct index array from a huge data array

To simplify the question suppose that I have an array of numbers like
[3, 7, 8, 3, 9, 9, ...]
Now I want to get an array of the index of
array.Distinct().OrderBy(x=>x)
For the example above we first get the result sorted array of [3, 7, 8, 9]. Then we can go through original array, find the index of the result sorted array, finally we get
[0, 1, 2, 0, 3, 3, ...]
This can be achieved as
var array = new[] {3, 7, 8, 3, 9, 9};
var sortedArray = array.Distinct().OrderBy(x => x).ToList();
var result = array.Select(x => sortedArray.IndexOf(x)).ToArray();
However when I have a HUGE array this will be extremely slow.
Is there a more efficient way to get the same result?
Note the sortedArray is huge too. (Data range is large)
Thanks.
You can convert the sorted array to a dictionary and search the index from it.
var i = 0;
var sortedDict = array.Distinct().OrderBy(x => x).ToDictionary(x => x, x => i++);
var result = array.Select(x => sortedDict[x]).ToArray();
If you want to save some memory spaces, you can also try BinarySearch.
var result = array.Select(x => sortedArray.BinarySearch(x)).ToArray();

C# Linq check the previous record to the current record and update the amount if duplicate

How can I use Linq to check the previous Account Number to see if it matches the current account number and if it does make the amount = 0?
Thanks,
Mark
If you have a list of Account Numbers. You could use Enumerable.Range for comparing to the previous value
var accountNumbers = new List<int> { 0, 1, 2, 3, 4, 5, 5, 5, 6, 7, 3 };
var result = Enumerable.Range(1, accountNumbers.Count - 1)
.Select(i => accountNumbers[i] != accountNumbers[i - 1] ? accountNumbers[i] : 0)
.ToList();
First, we need to clarify what "make the amount = 0" means. LINQ sequences are implicitly immutable, hence it is not possible to modify the amount in-place, but we need to return a new sequence (if you do want to modify the amounts in-place I would recommend using an array and a normal loop).
The answer provided by #Max works in principle, but is not performant as lists (and many other collection types) have only slow access to random elements.
LINQ does not have convenient tools for the task out of the box. Using only in-built methods a solution could be:
IEnumerable<int> accountNumbers = new List<int> { 0, 1, 2, 3, 4, 5, 5, 5, 6, 7, 3 };
IEnumerable<double> amounts = new List<double> { 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100 };
var possiblyInvalidAccountNumbers = accountNumbers.Skip(1); // The first account has no predecessor
var possiblyInvalidAmounts = amounts.Skip(1);
var validatedAmounts =
accountNumbers.Take(possiblyInvalidAccountNumbers.Count())
.Zip(possiblyInvalidAccountNumbers, (prevAccNo, accNo) => new { prevAccNo, accNo })
.Zip(possiblyInvalidAmounts, (numbers, amount) => numbers.prevAccNo != numbers.accNo ? amount : 0.0)
.Prepend(amounts.First());
Obviously, this code is somewhat convoluted, so a possible way to improve the clarity could be to write your own extension methods (e.g. a Zip3() method that takes three inputs would come in handy).

Reforming integer range to a smaller integer range (Unity (C#))

So, i have a set of integers in an list
public List<int> numbers = new List<int>() { 3, 7, 6, 9, 8, 10, 11 }
what i am wanting to do is change those numbers so they are ordered between 0 and 6, to set as siblingindexs.
and then would be changed to become
public List<int> newArrangedNumbers = new List<int>() {0, 2, 1, 4, 3, 5, 6}
But im really not sure how to do this... Anyone know?
P.S. i cant rearrange the numbers because then i would lose track of the game objects since the numbers themselves aren't actually in an array, but i have gameobjects in an array, and i find the "SortIndex" of each gameobject, which are the numbers from above, the order of the numbers in the array is actually the order of GameObjects in the array, which i need to keep the same.
Edit: i also cannot change them to float values because for some reason, when using SetSiblingIndex(int), you have to integers, you cant use floats
Edit 2: i am NOT trying to sort the numbers, i am trying to CONVERT the numbers from 3-11 into 0-6 in ORDER
From:
{3, 7, 6, 9, 8, 10, 11}
To:
{0, 2, 1, 4, 3, 5, 6}
Edit 3: Here is my script for testing
List<int> Indexs = new List<int>() { 4, 7, 56, 9, 65, 67, 8, 3, 6 };
var sorted = Indexs.Select((x, i) => new KeyValuePair<int, int>(x, i)).OrderBy(x => x.Key).ToList();
List<int> newArrangedNumbers = sorted.Select(x => x.Value).ToList();
for(int i = 0; i < newArrangedNumbers.Count; i++)
{
Debug.Log(Indexs[i] + " : " + newArrangedNumbers[i]);
}
When i only had 7 (0-6) indexs in the "Indexs" List it worked fine, but when i added any more, it started giving me the incorrect numbers
This is what it gives with this
Here is a good method to achieve your desired output from this stack form C# Sort list while also returning the original index positions?
The modified code for your solution is below.
//The original list
List<int> numbers = new List<int>() { 3, 7, 6, 9, 8, 10, 11 };
var sorted = numbers
.Select((x, i) => new KeyValuePair<int, int>(x, i))
.OrderBy(x => x.Key)
.ToList();
//The sorted list
List<int> numbersSorted = sorted.Select(x => x.Key).ToList();
//List of indexes sorted based on the list above
List<int> newArrangedNumbers = sorted.Select(x => x.Value).ToList();
Edit
Since you sort the list, but also retrieve the sorted indexes based on the list you just sorted, you aren't going to have any mixup with your game objects.
It sounds like you want to find positions of each element in a sorted list of the same items.
So sort and find where element is and assign the index. Code sample below assumes unique numbers:
var sorted = numbers.OrderBy(x=>x).ToArray();
var result = new int[numbers.Count];
for (var i = 0; i < numbers.Count; i++)
{
var index = number.IndexOf(sorted[i]);
result[index] = i;
}
Notes
for anything about 5-10 items I'd use dictionary instead of IndexOf if you want to stick with this code.
if numbers are not unique or performance is critical you need to use solution by Aaron Jones that eventually will track original indexes even if it is harder to understand.

skipWhile in LINQ is not working as excepted

I am trying to learn LINQ and found out from MSD documentation that SkipWhile will tend to skip the value as long as the statement is true. however when I use this statement below I am not getting the result as expected.
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
var allButFirst3Numbers = numbers.SkipWhile(x => x > 9);
foreach (var n in allButFirst3Numbers)
{
Console.WriteLine(n);
}
From the code above the result should be
1,2,3,4,5,6,7,8,9
But I am getting the result as
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
Can anyone point out what exactly am I doing wrong in this, and how to get the expected result.
From the docs for Enumerable.SkipWhile (emphasis mine):
This method tests each element of source by using predicate and skips the element if the result is true. After the predicate function returns false for an element, that element and the remaining elements in source are yielded and there are no more invocations of predicate.
So because the first element yields a false it will return everything. I suspect what you really wanted to write was:
var result = numbers.Where(x => x <= 9);
You are getting what you should get. You are saying skip while x > 9. 1 is less than 9 so it starts taking right away.
Try .SkipWhile(x=>x<=9) ... But that will give you 10,11,12,13,...
You may also try .TakeWhile(x=>x<=9) which will return 1,2,3,4,5,6,7,8,9
The said predicate x => x > 9 will be match in the starting of the list and once that predicate is false it will go ahead and consider all the elements. You should rather try to use Where() extension method like
var allButFirst3Numbers = numbers.Where(x => x > 9);
then you want to take, not skip
var numbers = Enumerable.Range(1, 15);
var result = numbers.TakeWhile(x => x <= 9);
Debug.Print(string.Join(", ", result)); // 1, 2, 3, 4, 5, 6, 7, 8, 9

Reverse Paging Using Linq

I have a scenario where I have to get paginated records in a reversed way using LINQ. Lets Assume I have 15 items in the order they were posted:
1, 2, 3, 4, 5.......... 15
Having a pages size of 5, if the user sends 1 as the currentPage in my method. Then I should be able to return 11, 12, 13, 14, 15 as the result set, if he sends 2 as the currentPage then I should return 6, 7, 8, 9, 10 and so on. How do I set the value that I give to the Skip method so that it would give me records in this way? And yes, I'm ordering the items in an ascending order by their dates too.
Just a simple example of the logic:
var nums = new[] {1, 2, 3, ..., 15};
var lastFew = nums.Reverse().Take(5).Reverse().ToArray();
Or alternatively, as a list:
var nums = new List<int> { 1, 2, 3, ..., 15};
nums.Reverse();
nums.RemoveRange(5); // removes from index 5 till end
nums.Reverse();
The actual implementation can vary depending on how your code is designed, etc, but that should get you started.
The key methods to use here are: Reverse, Take to limit the returned amount, and Skip to choose the starting index. If you use Skip, you can most likely avoid using Reverse altogether.
I would do like this (using EF):
var resultByDesc = db.Entities
.OrderByDescending(o => o.DateTime)
.Skip(page * itemsPerPage)
.Take(itemsPerPage)
.ToList(); // important! eager loading!
// it will be executed in your client side, not SQL.
var actualResult = resultByDesc.Reverse();

Categories

Resources