vb.netlinq

How to get the index of duplicate elements along with the range of empty elements after it. LINQ


Dim ComplexArray As String() = Split("A   C  A     B   C   A B  A  B    C")

Dim Need_result= {({"A", ({"0-2", "5-9", "16-16", "19-20"})}), ({"B", ({"10-12", "17-18", "21-24"})}), ({"C", ({"3-4", "13-15", "25-25"})})}

When we copy vertically merged cells in Excel, such an array is obtained.(ComplexArray) trying to get The number of duplicates in the column where the cells are randomly merged, then the difference between the rows of the duplicates and z-a Descending of the duplicate cell enter image description here


Solution

  • First of all, you mention 3D array, which hints at multidimensional. From the look of your question only 2D is required. This is just a terminology issue. However, from the look of your data, a multidimensional array will not work, as each letter could have a different number of results. A multidimensional array [n,m] will have n rows and m columns, so this will not work for you. I guess you are looking for a jagged array [n][m] in which m can vary for each n. It's an important distinction, from which stems your frustration with the community.

    Looking at your need,

    'Option Strict Off
    
    Dim Need_result = {({"A", ({"0-2", "5-9", "16-16", "19-20"})}), ({"B", ({"10-12", "17-18", "21-24"})}), ({"C", ({"3-4", "13-15", "25-25"})})}
    

    enter image description here

    I'm not going to bother with that data structure, sorry.

    I recommend you use a Dictionary. At least, since you asked for LINQ, I will make a one-liner in LINQ to produce the dictionary.

    Dim s = "A   C  A     B   C   A B  A  B    C"
    
    Dim r = s.Split(" "c).Select(Function(c, i) New Tuple(Of String, Integer)(c, i)).
            Where(Function(t) Not String.IsNullOrWhiteSpace(t.Item1)).
            Select(Function(t) (t.Item1, $"{t.Item2}-{t.Item2 + s.Split(" "c).Skip(t.Item2 + 1).TakeWhile(Function(c) String.IsNullOrWhiteSpace(c)).Count()}")).
            GroupBy(Function(t) t.Item1).
            Select(Function(g) (g.Key, g.Select(Function(t) t.Item2).ToArray())).
            OrderBy(Function(t) t.Key).
            ToDictionary(Function(kvp) kvp.Key, Function(kvp) kvp.ToArray)
    

    In multiple lines:

    Dim r1 = s.Split(" "c).Select(Function(c, i) New Tuple(Of String, Integer)(c, i))
    Dim r2 = r1.Where(Function(t) Not String.IsNullOrWhiteSpace(t.Item1))
    Dim r3 = r2.Select(Function(t) (t.Item1, $"{t.Item2}-{t.Item2 + s.Split(" "c).Skip(t.Item2 + 1).TakeWhile(Function(c) String.IsNullOrWhiteSpace(c)).Count()}"))
    Dim r4 = r3.GroupBy(Function(t) t.Item1)
    Dim r5 = r4.Select(Function(g) (g.Key, g.Select(Function(t) t.Item2).ToArray()))
    Dim r6 = r5.OrderBy(Function(t) t.Key)
    Dim r7 = r6.ToDictionary(Function(kvp) kvp.Key, Function(kvp) kvp.ToArray)
    

    Let's see the output

    For Each k In r.Keys
        Debug.Print($"{k}: {String.Join(", ", r(k))}")
    Next
    

    A: 0-2, 5-9, 16-16, 19-20
    B: 10-12, 17-18, 21-24
    C: 3-4, 13-15, 25-25

    Now compare the output with your Need_result,

    enter image description here

    It's the same data, with the added benefit of type-safety and the ability to key the data structure.

    What if you want to know how many "B" ranges there are?

    ' using Object()()
    Dim y = DirectCast(Need_result(Array.FindIndex(Need_result, Function(n) n(0).ToString() = "B"))(1), String()).Count()
    
    ' using Dictionary(String, String())
    Dim z = r("B").Length