Forum Discussion

FabvE's avatar
FabvE
Helper I
1 year ago
Solved

Remove rows based on same AND other column

Hi,

I'm fairly new to PowerBI and have a task to create a dashboard about surveys.

 

The data sets include the following columns:

  • uniqueId: as the name says an unique id for the entry
  • operation: either CreateForm (when a new survey is created) or CreateResponse (when an answer to a survey is sent)
  • formId: id of the form; when operation = CreateResponse then this response is for the corresponding operation = CreateForm 
  • formName: name of the survey
  • timestamp: when the entry was created
  • user: can be an email or plain text "anonymous"

 

Sample Table:

 

uniqueId

operationformIdformNametimestampuser

1

CreateResponseAWhat about lunch?2024-08-10user1
2CreateFormXWhich design is better?2024-08-12user2
3CreateResponseXWhich design is better?2024-08-13user3
4CreateResponseBWhen will we meet?2024-08-15user4
5CreateFormYFish or pork?2024-08-19user3
6CreateResponseXWhich design is better?2024-08-22user5
7CreateResponseYFish or pork?2024-08-28user3

 

I already sorted the list, filtered double uniqueIds, created additional columns but one thing is missing before I can generate reports from the dataset.

My problem is: the list also contains responses to surveys which are NOT listed/marked as CreateForm or are from surveys which were created before the first logs were delivered. So I need to remove all entries from the list which match:

  • operation = CreateResponse
    and
  • formID = formId where operation = CreateForm

In my sample above

  • rows 1, 4 should be removed
    • because there is no corresponding formID with CreateForm
  • rows 2, 5 must be kept
    • because operation = CreateForm
  • rows 3, 6, 7 must be kept
    • because operation = CreateResponse
      and
    • existence of coresponding formIds + operation = CreateForm (rows 2 and 5)

I hope the sample makes it much clearer.

I already tried multiple filters and whatever and also consulted the web but I think I searched wrong.

 

Thanks in advance

 

Edit: added sample table and additional description

  • The Source line, where you see the Base64 conversion, is what you get when you paste the data into a table in PowerBI/PowerQuery. You should replace the Source line with your own source. You can read the M code documentation for the Binary.FromText function, but that Base64 argument merely determines the type of Binary encoding to be used.

     

    And either you didn't run the code, or I misunderstood your before and after.

     

    Source Step:

    And you wrote: remove rows 1,4 and keep 2,3,5,6,7

     

    After running code:

    Did I misunderstand what you wanted?  Or did you not run the code to see what it did?

     

    If I misunderstood, what rows did you wish to keep? As the code I provided clearly keeps 2,3,5,6 and 7

     

  • Anonymous's avatar
    Anonymous
    1 year ago

    Hi FabvE ,
    Thanks for ronrsnfld reply.
    You can try the following code

    let
        Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("ndBRC4IwEAfwr3LsOVCnlj1FBX6AICrEB7WjjXSTbeLXbwwJs0HQw8F2cL/9b0VBIrIiR4WVwRPqXgqNtrG3dWGVgaqWg4F2EA3b2V4WRGFAQ5rY81mjiki5Kgh9E7lUnb1c3ThvGNxR84cArqFGY1BNCJ0j1CGxL8dPKJ5DsYMSH3RwEAoYedvCiNAhmslI50bijHS50c1WzjUDqaCX6jmNbr+fX/+zB/34kNRBGx/kz0GzRY7yBQ==", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [#"Unique ID" = _t, Operation = _t, #"From ID" = _t, #"Form Name" = _t, Timestamp = _t, User = _t]),
        AddFormID = Table.AddColumn(Source, "FormID", each if [Operation] = "CreateForm" then [From ID] else null),
        FillDownFormID = Table.FillDown(AddFormID,{"FormID"}),
        FilterCreateResponse = Table.SelectRows(FillDownFormID, each [Operation] = "CreateResponse"),
        AddCustom = Table.AddColumn(FilterCreateResponse, "HasCorrespondingForm", each List.Contains(FillDownFormID[FormID], [From ID])),
        RemoveInvalidResponses = Table.SelectRows(AddCustom, each [HasCorrespondingForm] = true),
        CombineTables = Table.Combine({Table.SelectRows(FillDownFormID, each [Operation] = "CreateForm"), RemoveInvalidResponses}),
        SortByUniqueID = Table.Sort(CombineTables,{{"Unique ID", Order.Ascending}}),
        RemoveHelperColumns = Table.RemoveColumns(SortByUniqueID,{"FormID", "HasCorrespondingForm"})
    in
        RemoveHelperColumns

    Final output

    Best regards,
    Albert He


    If this post helps, then please consider Accept it as the solution to help the other members find it more quickly

  • Ok, after many tries and more fails I went with another approach which solved my problem.

    I created an addinional query which contained only the created records:

    let
    Quelle = Table.SelectRows(Auswertung_raw, each [Operation] = "CreateForm")
    in
    Quelle

    I then used Merge queries to do a join between the initial table Auswertung_raw and the CreateForm-Table:

    let
    Quelle = Table.NestedJoin(Auswertung_raw, {"FormId"}, tblErstellteUmfragen, {"FormId"}, "tblErstellteUmfragen", JoinKind.LeftSemi),
    #"Entfernte Duplikate" = Table.Distinct(Quelle, {"RecordId"})
    in
    #"Entfernte Duplikate"

    This gives me a table with all responses which have a corresponding CreateForm + the initial CreateForm-entries.

     

    Nevertheless, thank you all - especially ronrsnfld- for your help and time! 👍 

17 Replies

  • Hello FabvE 

    Your query is a bit unclear to me. It would be helpful if you could include a sample dataset with your question, as well as an example of the expected outcome. This information would greatly assist in clarifying the issue.

     

    Thanks,
    Udit

    • FabvE's avatar
      FabvE
      Helper I

      Hi, I added a sample table. Hope this clarifies my question. Thx

      • ronrsnfld's avatar
        ronrsnfld
        Super User

        I reversed your logic so we instead select rows where, for the same formId 

        • keep rows where operation = "CreateForm"
        • if operation = "CreateResponse" then there must also be another entry with "CreateForm"
        • (I did not know how to interpret your requirement surveys which were created before the first logs were delivered)

        Here's one way putting your logic into a Table.SelectRows function:

         

        let
            Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("ndBRC4IwEAfwr3LsWVCnlj1FBX6AXirEB7WjjXSTbeLXbwwJs0HQw8F2cL/9b2VJYhKQk8La4Bn1IIVG2zjYurDaQN3I0UA3ipbtbS8P4yikEU3tedSoYlIFJaFvopCqt5erG+ctgztq/hDANTRoDKoZoUuEOiTx5fgJJUsocVDqg44OQgET7zqYEHpEMxvZ0kidka03utkquGYgFQxSPefR3ffzm3/2oB8fkjlo64P8OWi+ylG9AA==", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [uniqueId = _t, operation = _t, formId = _t, formName = _t, timestamp = _t, user = _t]),
            #"Changed Type" = Table.TransformColumnTypes(Source,{
                {"uniqueId", Int64.Type}, {"operation", type text}, {"formId", type text}, 
                {"formName", type text}, {"timestamp", type date}, {"user", type text}}),
        
            #"Remove Rows" = Table.SelectRows(#"Changed Type",(r)=> 
                r[operation] = "CreateForm" 
                or (r[operation] = "CreateResponse" 
                        and List.Contains(Table.SelectRows(#"Changed Type", each [formId]=r[formId])[operation],"CreateForm")))
        in
            #"Remove Rows"

         

  • Anonymous's avatar
    Anonymous
    Not applicable

    Hi FabvE ,
    Thanks for ronrsnfld reply.
    You can try the following code

    let
        Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("ndBRC4IwEAfwr3LsOVCnlj1FBX6AICrEB7WjjXSTbeLXbwwJs0HQw8F2cL/9b0VBIrIiR4WVwRPqXgqNtrG3dWGVgaqWg4F2EA3b2V4WRGFAQ5rY81mjiki5Kgh9E7lUnb1c3ThvGNxR84cArqFGY1BNCJ0j1CGxL8dPKJ5DsYMSH3RwEAoYedvCiNAhmslI50bijHS50c1WzjUDqaCX6jmNbr+fX/+zB/34kNRBGx/kz0GzRY7yBQ==", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [#"Unique ID" = _t, Operation = _t, #"From ID" = _t, #"Form Name" = _t, Timestamp = _t, User = _t]),
        AddFormID = Table.AddColumn(Source, "FormID", each if [Operation] = "CreateForm" then [From ID] else null),
        FillDownFormID = Table.FillDown(AddFormID,{"FormID"}),
        FilterCreateResponse = Table.SelectRows(FillDownFormID, each [Operation] = "CreateResponse"),
        AddCustom = Table.AddColumn(FilterCreateResponse, "HasCorrespondingForm", each List.Contains(FillDownFormID[FormID], [From ID])),
        RemoveInvalidResponses = Table.SelectRows(AddCustom, each [HasCorrespondingForm] = true),
        CombineTables = Table.Combine({Table.SelectRows(FillDownFormID, each [Operation] = "CreateForm"), RemoveInvalidResponses}),
        SortByUniqueID = Table.Sort(CombineTables,{{"Unique ID", Order.Ascending}}),
        RemoveHelperColumns = Table.RemoveColumns(SortByUniqueID,{"FormID", "HasCorrespondingForm"})
    in
        RemoveHelperColumns

    Final output

    Best regards,
    Albert He


    If this post helps, then please consider Accept it as the solution to help the other members find it more quickly

    • ronrsnfld's avatar
      ronrsnfld
      Super User

      Interesting algorithm. Kindly explain the advantage over what I supplied since yours seems to execute quite a bit more slowly using the Power Query diagnostic tools.

  • wdx223_Daniel's avatar
    wdx223_Daniel
    Community Champion

    =let a=Table.SelectRows(YourTable,each [operation]="CreateForm")[formId] in Table.SelectRows(YourTable,each [operation]="CreateForm" or List.Contains(a,[formId]))

    • FabvE's avatar
      FabvE
      Helper I

      I tried this but ended up dismissing your solution. The refreshing took many minutes for just 4 rows so I canceled it... 😞

  • Ok, after many tries and more fails I went with another approach which solved my problem.

    I created an addinional query which contained only the created records:

    let
    Quelle = Table.SelectRows(Auswertung_raw, each [Operation] = "CreateForm")
    in
    Quelle

    I then used Merge queries to do a join between the initial table Auswertung_raw and the CreateForm-Table:

    let
    Quelle = Table.NestedJoin(Auswertung_raw, {"FormId"}, tblErstellteUmfragen, {"FormId"}, "tblErstellteUmfragen", JoinKind.LeftSemi),
    #"Entfernte Duplikate" = Table.Distinct(Quelle, {"RecordId"})
    in
    #"Entfernte Duplikate"

    This gives me a table with all responses which have a corresponding CreateForm + the initial CreateForm-entries.

     

    Nevertheless, thank you all - especially ronrsnfld- for your help and time! 👍