skip to Main Content

I have this list of lists structure:

[
  ["nginx-66b6c48dd5-25wv5", "nginx-deployment", "worker-1", "0", "2"],
  ["nginx-66b6c48dd5-2nhbs", "nginx-deployment", "worker-1", "0", "2"],
  ["nginx-66b6c48dd5-5b4dw", "nginx-deployment", "worker-1", "0", "2"],
  ["nginx-66b6c48dd5-p7sx9", "nginx-deployment", "worker-1", "0", "2"],          
  ["coredns-autoscaler-76f8869cc9-gd69j", "kube-system", "worker-1", "1", "5"],
  ["coredns-55b58f978-q2skn", "kube-system", "worker-1", "7", "11"] 
]

And I want to merge them by the second list item (nginx-deployment, kube-system, etc) and sum the two latest items and remove the first item.

So it would look like this:

[
  ["nginx-deployment", "worker-1", "0", "8"],
  ["kube-system", "worker-1", "8", "16"] 
]

Enum.zip kinda works but I would have to first split the sub lists and I think there has to be a better way to do it.

2

Answers


  1. I would use Enum.reduce for this one, converting the list to a map in the process.

    The idea would be to "loop" over the list, each time updating an "accumulator" map with the sums, like this:

    [
      ["nginx-66b6c48dd5-25wv5", "nginx-deployment", "worker-1", "0", "2"],
      ["nginx-66b6c48dd5-2nhbs", "nginx-deployment", "worker-1", "0", "2"],
      ["nginx-66b6c48dd5-5b4dw", "nginx-deployment", "worker-1", "0", "2"],
      ["nginx-66b6c48dd5-p7sx9", "nginx-deployment", "worker-1", "0", "2"],          
      ["coredns-autoscaler-76f8869cc9-gd69j", "kube-system", "worker-1", "1", "5"],
      ["coredns-55b58f978-q2skn", "kube-system", "worker-1", "7", "11"] 
    ]
    |> Enum.reduce(%{}, fn [_, key, _, stat0, stat1], accumulator ->                                                   
      int0 = String.to_integer(stat0)
      int1 = String.to_integer(stat1)
      Map.update(accumulator, key, {int0, int1}, fn {x, y} -> {x + int0, y + int1} end)
    end)
    

    That would return:

    %{"kube-system" => {8, 16}, "nginx-deployment" => {0, 8}}
    

    Note: I didn’t include the "3rd" field because I’m not sure how that should be selected. Is it always going to be unique? Anyway, my answer outlines how I would approach this.

    Update:

    I’ve been working with Elixir ever since I wrote my original answer and I have come to appreciate using reduce in for expressions so I’m going to re-write my answer in what I think is a more readable format, for posterity.

    This is called a "list comprehension".

    data = [
      ["nginx-66b6c48dd5-25wv5", "nginx-deployment", "worker-1", "0", "2"],
      ["nginx-66b6c48dd5-2nhbs", "nginx-deployment", "worker-1", "0", "2"],
      ["nginx-66b6c48dd5-5b4dw", "nginx-deployment", "worker-1", "0", "2"],
      ["nginx-66b6c48dd5-p7sx9", "nginx-deployment", "worker-1", "0", "2"],          
      ["coredns-autoscaler-76f8869cc9-gd69j", "kube-system", "worker-1", "1", "5"],
      ["coredns-55b58f978-q2skn", "kube-system", "worker-1", "7", "11"] 
    ]
    
    for [_, key, _, stat0, stat1] <- data, reduce: %{} do
      accumulator ->                                                
        int0 = String.to_integer(stat0)
        int1 = String.to_integer(stat1)
        Map.update(accumulator, key, {int0, int1}, fn {x, y} -> {x + int0, y + int1} end)
    end
    
    Login or Signup to reply.
  2. If the third field might be not unique, the solution by @PeacefulJames (which is otherwise absolutely correct,) might be too fragile due to hard-coded stuff. Here is more verbose, but better extendable solution, involving Enum.group_by/3.

    list
    |> Enum.group_by(
      &Enum.slice(&1, 1..2),
      fn e ->
        e
        |> Enum.take(-2)
        |> Enum.map(&String.to_integer/1)
      end
    ) |> Enum.map(fn {k, v} ->
      k ++ Enum.reduce(v, [0, 0], fn [x, y], [xx, yy] -> [x + xx, y + yy] end) 
    end)
      
    #⇒ [["kube-system", "worker-1", 8, 16],
    #   ["nginx-deployment", "worker-1", 0, 8]]
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search