Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

List operations related to another list's conditions

I have two lists like these:

public static List<List<List<double>>> listQ = new List<List<List<double>>>();
public static List<List<List<double>>> listVal = new List <List<List<double>>>();

enter image description here

2 list also have same size. I want some operations in listQ relation to listVal:

  • if listVal's any indexes have same values, I want to take average listQ's values in same indexes.

For example:

listVal[0][0][ 1] = listVal[0][0][2] = 3. 

So, I want listQ's same indexes be same.

(listQ[0][0][ 1] + listQ[0][0][2]) / 2 = (26.5 + 20.4) / 2 = 23.45. 

Now, each these indexes must have 23.45 :

listQ[0][0][ 1] = 23.45

listQ[0][0][ 2] = 23.45

Likewise;

listVal[0][2][0] = listVal[0][2][2] = 1. Therefore listQ[0][2][0] and listQ[0][2][2] 's average must be taken.

How can I do this?

EDIT : @Roman Izosimov 's and @Petko Petkov 's solutions are working correctly. Which one has higher performance? What do you think?

like image 710
1teamsah Avatar asked Jan 15 '14 07:01

1teamsah


3 Answers

LINQ solutions for lulz :)

List<List<List<double>>> listVal = new List<List<List<double>>>(){
    new List<List<double>>{
        new List<double>(){1,1,3},
        new List<double>(){2,1,2},
        new List<double>(){1,2,3}
    },
    new List<List<double>>{
        new List<double>(){2,1,3},
        new List<double>(){2,4,2},
        new List<double>(){3,1,3}
    },
    new List<List<double>>{
        new List<double>(){4,1,1},
        new List<double>(){4,2,1},
        new List<double>(){4,3,1}
    }
};
List<List<List<double>>> listQ = new List<List<List<double>>>(){
        new List<List<double>>{
        new List<double>(){3,7,4},
        new List<double>(){8,15,23},
        new List<double>(){11,13,17}
    },
    new List<List<double>>{
        new List<double>(){90,3,7},
        new List<double>(){5,7,12},
        new List<double>(){7,14,21}
    },
    new List<List<double>>{
        new List<double>(){32,4,1},
        new List<double>(){55,12,8},
        new List<double>(){3,5,8}
    }
};


//Linq awesomeness
var qry = listVal.SelectMany((l1, i0) =>
                l1.SelectMany((l2, i1) =>
                    l2.Select((ele, i2) =>
                        new { i0, i1, i2, gVal = ele, qVal = listQ[i0][i1][i2] })))
                .GroupBy(x => new { x.i0, x.i1, x.gVal }) // if you want to average across the innermost lists only 
                //.GroupBy(x => x.gVal)                   //if you want to average acreoss the whole data
                .SelectMany(x => x.Select(e => new { e.i0, e.i1, e.i2, avg = x.Average(y => y.qVal) }));
foreach (var e in qry)
{
    listQ[e.i0][e.i1][e.i2] = e.avg;
}
like image 182
Petko Petkov Avatar answered Nov 14 '22 08:11

Petko Petkov


Your problem is similar to count the frequences of elements in a list.
Your can use a Dictionary<double,List<int[]> with the double key is value in listVal, and List<int[]> stores the positions of that value in listVal.

Here my sample code:

//we store list of indices with the same value in a dictionary
Dictionary<double, List<int[]>> dictionary = new Dictionary<double, List<int[]>>();

//loop over listVal
for (int i = 0; i < listVal.Count; i++)
    for (int j = 0; j < listVal[i].Count; j++)
        for (int k = 0; k < listVal[i][j].Count; k++)
        {
            if (!dictionary.ContainsKey(listVal[i][j][k]))
            {
                //if the dictionary doesn't have value listVal[i][j][k] 
                //then add the value as key with the value's current index
                dictionary.Add(listVal[i][j][k], new List<int[]> { new int[] { i, j, k } });
            }
            else
            {
                //add more index of the same value to the index list
                dictionary[listVal[i][j][k]].Add(new int[] { i, j, k });
            }
        }

foreach (var entry in dictionary)
{
    //get list of all position having the same value
    List<int[]> indices = entry.Value;

    //do your process on listQ
    //sum values in listQ
    double sum = 0;
    foreach (var index in indices)
    {
        sum += listQ[index[0]][index[1]][index[2]];
    }
    //calculate average value
    double average = sum / indices.Count;

    //assign back to litsQ
    foreach (var index in indices)
    {
        listQ[index[0]][index[1]][index[2]]  = average;
    }


}
like image 33
vutran Avatar answered Nov 14 '22 07:11

vutran


In my solution you can have any numbers of elements in List<double> in listQ and listVal, but in both lists elements count mus be same.

Algorithm:

for (var i1 = 0; i1 < listVal.Count; i1++)
            for (var i2 = 0; i2 < listVal[i1].Count; i2++)
            {
                var alreadyInList = new Dictionary<double, List<Tuple<int, int, int>>>();
                for (var d = 0; d < listVal[i1][i2].Count; d++)
                {
                    var decVal = listVal[i1][i2][d];
                    if (!alreadyInList.ContainsKey(decVal))
                    {
                        alreadyInList.Add(decVal, new List<Tuple<int, int, int>>());
                        continue;
                    }
                    var firstMatchElIndex = listVal[i1][i2].IndexOf(decVal);
                    if (alreadyInList[decVal].All(x => x.Item3 != firstMatchElIndex))
                        alreadyInList[decVal].Add(new Tuple<int, int, int>(i1, i2, firstMatchElIndex));

                    alreadyInList[decVal].Add(new Tuple<int, int, int>(i1, i2, d));
                }

                SetListQ(listQ, alreadyInList.Where(x => x.Value.Count > 1).Select(x => x.Value));
            }

private void SetListQ(List<List<List<double>>> listQ, IEnumerable<List<Tuple<int, int, int>>> matches)
    {
        foreach (var match in matches)
        {
            var sum = match.Sum(tuple => listQ[tuple.Item1][tuple.Item2][tuple.Item3]);
            var avg = sum / match.Count;
            foreach (var tuple in match)
                listQ[tuple.Item1][tuple.Item2][tuple.Item3] = avg;
        }
    }

And check:

Console.WriteLine("listQ[0][0][0] = {0}", listQ[0][0][0]);
Console.WriteLine("listQ[0][0][1] = {0}", listQ[0][0][1]);
Console.WriteLine("listQ[0][0][2] = {0}", listQ[0][0][2]);
//....

Using your data:

var listQ = new List<List<List<double>>>
        {
            new List<List<double>>
            {
                new List<double>{20.9, 26.5, 20.4},
                new List<double>{18.3, 19.7, 22.2},
                new List<double>{12.7, 20.6, 25.6},
                new List<double>{17.1},
                new List<double>{17.3},
                new List<double>{16.9}
            }
        };

        var listVal = new List<List<List<double>>>
        {
            new List<List<double>>
            {
                new List<double>{1, 3, 3},
                new List<double>{1, 3, 3},
                new List<double>{1, 3, 1},
                new List<double>{1},
                new List<double>{3},
                new List<double>{3}
            }
        };

We have in Console:

listQ[0][0][0] = 20.9
listQ[0][0][1] = 23.45
listQ[0][0][2] = 23.45
listQ[0][1][0] = 18.3
listQ[0][1][1] = 20.95
listQ[0][1][2] = 20.95
listQ[0][2][0] = 19.15
listQ[0][2][1] = 20.6
listQ[0][2][2] = 19.15

For example I added some additional data:

var listQ = new List<List<List<double>>>
        {
            new List<List<double>>
            {
                new List<double>{20.9, 26.5, 20.4, 10.0},
                new List<double>{18.3, 19.7, 22.2, 12.22},
                new List<double>{12.7, 20.6, 25.6, 20.6},
                new List<double>{17.1},
                new List<double>{17.3},
                new List<double>{16.9}
            }
        };

        var listVal = new List<List<List<double>>>
        {
            new List<List<double>>
            {
                new List<double>{1, 3, 3, 1},
                new List<double>{1, 3, 3, 3},
                new List<double>{1, 3, 1, 2},
                new List<double>{1},
                new List<double>{3},
                new List<double>{3}
            }
        };

After processing we have:

listQ[0][0][0] = 15.45
listQ[0][0][1] = 23.45
listQ[0][0][2] = 23.45
listQ[0][0][3] = 15.45
listQ[0][1][0] = 18.3
listQ[0][1][1] = 18.04
listQ[0][1][2] = 18.04
listQ[0][1][3] = 18.04
listQ[0][2][0] = 19.15
listQ[0][2][1] = 20.6
listQ[0][2][2] = 19.15
listQ[0][2][3] = 20.6

This is not whery nice solution but it works and use minimum iterations count. I hope this solution will solve your task.

like image 29
Roman Izosimov Avatar answered Nov 14 '22 08:11

Roman Izosimov