4

I have a "Element" class with a "Index" property which is used for sorting a list of elements. When i'm adding elements to the list I want to spread out the new elements evenly according to existing elements in the list.

This means if I have 6 elements and want to add 3 elements the result should look like the image below:

enter image description here

The problem I have with my code (See below) so far is it uses the wrong index, so if I have 2 existing elements and add 9 elements, the last element index is 18 which I don't really understand.

public List<Element> AddElements()
{   
    // The elements are inserted before this
    List<Element> existingElements = new List<Element>();
    List<Element> elementsToAdd = new List<Element>();

    int elementsLeft = 1;

    foreach (Element element in elementsToAdd)
    {
        // Generate the next index
        int nextIndex = 1;

        // Only proceed if any elements exists
        if (existingElements.Count > 0)
        {
            // divisonResult = 12 / 4 = 3
            double divisonResult = Math.Floor(Convert.ToDouble(existingElements.Count) / Convert.ToDouble(elementsToAdd.Count));

            // modulusResult = 12 % 2 = 0
            double modulusResult = Convert.ToDouble(existingElements.Count) % Convert.ToDouble(elementsToAdd.Count);

            // NextPosition = (3 + 1) * 1 = 4
            // NextPosition = (3 + 1) * 2 = 8
            // NextPosition = (3 + 1) * 3 = 12
            // NextPosition = (3 + 1) * 4 = 16
            if (modulusResult <= 0 && elementsToAdd.Count > 1)
                nextIndex = Convert.ToInt16(divisonResult) * elementsLeft;
            else
                nextIndex = (Convert.ToInt16(divisonResult) + 1) * elementsLeft;

            elementsLeft++;

            // Move existing elements
            var elementsToBeMoved = existingElements.Where(elementQuery => elementQuery.Index >= nextIndex);

            foreach (Element elementToBeMoved in elementsToBeMoved)
            {
                elementToBeMoved.Index++;
            }
        }

        // Add element to existing elements
        existingElements.Add(new Element { Index = nextIndex });
    }

    // Return new list
    return existingElements;
}
Dumpen
  • 1,622
  • 6
  • 22
  • 36
  • 1
    What is this for? Not being flippant, I just think the request is interesting, but I can't see what you'd use it for immediately. – Adam Houldsworth Dec 05 '13 at 13:42
  • That sounds like a bad idea. You're going to get awful performance on inserts in List, because it has to copy all the items after the one you're inserting first. If your list is longer than a few items, it might be a bottleneck. What problem are you trying to solve? There's probably a better way to do it than by spreading items in a list evenly... – Luaan Dec 05 '13 at 13:43
  • `Convert.ToDouble(existingElements.Count) / Convert.ToDouble(elementsToAdd.Count)` this is just: `1.0 * existingElements.Count / elementsToAdd.Count`. Or maybe use the `(double)` cast. Why all people insist on using `Convert` machinery for such simple things ;0 – BartoszKP Dec 05 '13 at 13:44
  • Adam: It's used for inserting different kinds of elements where one type of element needs to spread out evenly. More specifically it's for an ad system in my company :-) Luaan: I am trying to spread them evenly, that is the problem. It's a feature i'm trying to implement. – Dumpen Dec 05 '13 at 13:45
  • Have a look at my proposition. It might involve starting over, but overall you might end up with something tidier. – Francis Ducharme Dec 05 '13 at 14:51

3 Answers3

1

Divide your original number of elements by the list you want to mix it with. 6/3 + 1=3(every 3rd item will be from list2). Run a loop for(var i = 0; i < list1.Count + list2.Count; i++) Within each loop check if the position of the new list is at the point where you should insert the item from list2 otherwise insert the next item from list1. Here it is as an extension method...

class Program
{
    static void Main(string[] args)
    {
        var existingElements  = new List<int> { 1, 2, 3, 4, 5, 6 };
        var elementsToAdd = new List<int> { 100, 101, 102 };
        existingElements = existingElements.Mix(elementsToAdd).ToList();
        Console.WriteLine(String.Join(", ", existingElements));
        Console.ReadKey();
    }
}

public static class ExtensionMethods
{
    public static IEnumerable<T> Mix<T>(this IEnumerable<T> source, IEnumerable<T> mix)
    {
        var list1 = source.ToArray();
        var list2 = mix.ToArray();
        var total = list1.Count() + list2.Count();
        var skip = (list1.Count() / list2.Count()) + 1;
        var count1 = 0;
        var count2 = 0;
        var finalList = new List<T>();

        for (var i = 0; i < total; i++)
        {
            var count = i + 1;
            if (count % skip == 0)
            {
                finalList.Add(list2[count2]);
                count2++;
            }
            else
            {
                finalList.Add(list1[count1]);
                count1++;
            }
        }

        return finalList;
    }
}
Rush Frisby
  • 11,388
  • 19
  • 63
  • 83
  • Thanks, i'll mark your answer as accepted since it's probably the best one i'll find. On a side note, how did Visual Studio knew where to set your breakpoints just from me copying the code? – Dumpen Dec 05 '13 at 14:16
  • Your breakpoints must have overlaid nicely with what I wrote and I think VS does adjust them slightly when you paste. – Rush Frisby Dec 05 '13 at 14:19
0

Aren't you looking for temporal ordering? This suggests to use KeyCollection.

How to insert as first element in dictionary?

The InsertItem method looks interesting.

Community
  • 1
  • 1
Francis Ducharme
  • 4,848
  • 6
  • 43
  • 81
0

I develop it like this:

public static class IEnumerableExtender
{
    public static IEnumerable<T> Mix<T>(this IEnumerable<T> first, IEnumerable<T> second)
    {
        var firstCount = first.Count();
        var secondCount = second.Count();

        // it is important that `first` is equal or larger
        // than `second`, if it is not, we swap
        if (firstCount < secondCount)
        {
            var a = first;
            first = second;
            second = a;
            firstCount = first.Count();
            secondCount = second.Count();
        }

        // at every `N` number of elements we will insert
        // one from the `second` list
        var insertAtEvery = Math.Floor(firstCount / (double)secondCount) + 1;

        int totalLength = firstCount + secondCount;

        var listResult = new List<T>(totalLength);

        for (int i = 0, x = 0, y = 0; i < totalLength; ++i)
        {
            // if it is time to insert an element from `second`
            // and there is still something to be inserted
            if ((i % insertAtEvery) == 0 && y < secondCount)
            {
                // insert and move the index from the `second`
                listResult.Add(second.ElementAt(y++));
            }
            else
            {
                // insert and move the index from the `first`
                listResult.Add(first.ElementAt(x++));
            }
        }

        return listResult;
    }
}

And here just a console code to test the output:

public class Program
{
    static void Main(string[] args)
    {
        int size1, size2;

        while (true)
        {
            Console.Write("Size of list #1: ");
            int.TryParse(Console.ReadLine(), out size1);

            if (size1 <= 0)
                break;

            Console.Write("Size of list #2: ");
            int.TryParse(Console.ReadLine(), out size2);

            if (size2 <= 0)
                break;

            var list1 = Enumerable.Range(0, size1).Select(o => '.').ToList();
            var list2 = Enumerable.Range(0, size2).Select(o => '#').ToList();

            var result = list1.Mix(list2);

            Console.WriteLine(String.Join(" ", result));
            Console.WriteLine();
        }
    }
}

Sample output

output

BrunoLM
  • 97,872
  • 84
  • 296
  • 452