How to move only one cell of a column and keep other columns same - excel

I hope someone can help me. Currently I have a table in which I need to move up or erase the first null of the last column only. This will let me to have all data in the same row.
enter image description here
I used conditional columns but I dont know how to refer the next row.
= Table.AddColumn(#"Changed Type", "Custom", each if [Column5] = null then [Column5] else 1)
enter image description here

This moves the rows up one in the last column.
let Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
x = {List.Combine({List.RemoveFirstN(List.Last(Table.ToColumns(Source)),1),{null}})},
y = List.RemoveLastN(Table.ToColumns(Source),1),
custom = Table.FromColumns(List.Combine({y,x}),Table.ColumnNames(Source))
in custom
This adds a new column with the former last column shifted up one row
let Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
columnname=List.Last(Table.ColumnNames(Source)),
shiftedList = List.RemoveFirstN(Table.Column(Source,columnname),1) & {null},
custom1 = Table.ToColumns(Source) & {shiftedList},
custom2 = Table.FromColumns(custom1,Table.ColumnNames(Source) & {"ShiftedRow"})
in custom2

Related

Move all text values with the same ID field to separate columns

I would like to move all the comments (Column B3:B14) to be new columns against each unique ID (Column A3:A14).
The Desired Format shows the layout that I would like to get to.
Hopefully that makes sense.
EDIT: This will do what you want using vba:
Option Explicit
Sub TransposeComments()
Dim inSR%, inTR%, inTC%, rgSource As Range, rgTarget As Range
Set rgSource = Range("A3") 'Change this if the 1st ID in the source table is moved
Set rgTarget = Range("D3") 'Change this to start populating at another start point
inTR = -1
Do
If rgSource.Offset(inSR) <> rgSource.Offset(inSR - 1) Then
inTR = inTR + 1: inTC = 2
rgTarget.Offset(inTR) = rgSource.Offset(inSR)
rgTarget.Offset(inTR, 1) = rgSource.Offset(inSR, 1)
Else
rgTarget.Offset(inTR, inTC) = rgSource.Offset(inSR, 1)
inTC = inTC + 1
End If
inSR = inSR + 1
''' End on 1st empty ID (assumes ID's in source data are contiguous and nothing is below them)
Loop Until rgSource.Offset(inSR) = ""
End Sub
I've assumed you know how to implement and call/run the vb. If not, let me know and I try and help with that. :)
============================================================
EDIT: How to do it all with formulas?
I'm unsure of how dynamic the extraction table has to be (as you don't say). For example:
o Will you be making a new extraction each time or will build a standing extractor table
o Will the source data vary in size (so you need to grow and shrink the 'lookup' range)
o Etc.
Given this, I've aimed for a solution that works and is adaptable. I'll leave it to you to adapt as appropriate 😊
To extract the unique serial numbers:
{=IFERROR(INDEX($A$2:$A$14, MATCH(0, COUNTIF($E$2:E2, $A$2:$A$14), 0)),"")}
To extract the corresponding comments:
{=IF($E3="","",IF(SUM(IF($A$2:$A$15=$E3,1))>=COUNTA($F$2:F$2),INDEX($B$2:$B$15,MATCH($E3,$A$2:$A$15,0)+COUNTA($F$2:F$2)-1),""))}
Notice the {}. Both are array formulas (entered with Ctrl, Shift and Enter)
Pictogram:
Addition Information:
The solution proposed assumes any same-serial-numbers are contiguous (as shown in your example.
If that's not the case by default, you'll have to sort the source date so it is.
You can obtain your desired output using Power Query, available in Windows Excel 2010+ and Office 365 Excel
Select some cell in your original table
Data => Get&Transform => From Table/Range
When the PQ UI opens, navigate to Home => Advanced Editor
Make note of the Table Name in Line 2 of the code.
Replace the existing code with the M-Code below
Change the table name in line 2 of the pasted code to your "real" table name
Examine any comments, and also the Applied Steps window, to better understand the algorithm and steps
M Code
let
//read in the data
//change table name in next line to actual table name in your workbook
Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
//set data types
#"Changed Type" = Table.TransformColumnTypes(Source,{{"ID", Int64.Type}, {"Comments", type text}}),
//group by ID and concatenate the comments with a character not used in the comments
//I used a semicolon, but that could be changed
#"Grouped Rows" = Table.Group(#"Changed Type", {"ID"}, {
{"Comment", each Text.Combine([Comments],";")},
//also generate Count of the number of comments in each ID group
//as the Maximum will be the count of the number of columns to eventually create
{"numCols", each Table.RowCount(_)}
}),
//calculate how many columns to create and delete that column
maxCols = List.Max(#"Grouped Rows"[numCols]),
remCount = Table.RemoveColumns(#"Grouped Rows","numCols"),
//Split into new columns
#"Split Column by Delimiter" = Table.SplitColumn(remCount, "Comment",
Splitter.SplitTextByDelimiter(";", QuoteStyle.Csv),maxCols)
in
#"Split Column by Delimiter"
If you have Excel for Microsoft 365 on the Mac with the FILTER and UNIQUE functions, you can use:
D23: =UNIQUE(Table1[ID]) *or some other cell8
and in the adjacent column:
=TRANSPOSE(FILTER(Table1[Comments],Table1[ID]=D23))

Increment difference between cells

I'm trying to duplicate data in a sheet with increments of 12 between each cell from a sheet with 1 cell per row. Between the 12-incremented rows there's other data. This means I can't drag to extend the formula. Like this for customer numbers:
'SheetA'E3 = 'SheetB'Y2
'SheetA'E15 = 'SheetB'Y3
'SheetA'E27 = 'SheetB'Y4
..and so on. I've tried extending 12/24 cells at a time and copying but I can't make it work. Extending doesn't add +1 to one sheet, just +12/+24 to both. Doing this manually will take months. Can this be done without a VBA solution?
Any suggestions? I'm sorry if my terminology isn't on point here.
SheetA:
Try this (run as VBA code):
Sub test1()
For i01 = 0 To 100
Worksheets("SheetA").Cells(3 + 12 * i01, 5) = Worksheets("SheetB").Cells(2 + i01, 25)
Next i01
End Sub
Power Query, available in Windows Excel 2010+ and Office 365, can produce your SheetA given SheetB. Not sure about the effect of the variability you mention.
The query assumes that the correct parameters are listed as column headers in Sheet B. The column headers will get copied over as parameters to sheet A.
To use Power Query:
Select some cell in your Data Table
Data => Get&Transform => from Table/Range
When the PQ Editor opens: Home => Advanced Editor
Make note of the Table Name in Line 2
Paste the M Code below in place of what you see
Change the Table name in line 2 back to what was generated originally.
Read the comments and explore the Applied Steps to understand the algorithm
M Code
let
//Read in the data
//Change table name in next line to be the "real" table name
Source = Excel.CurrentWorkbook(){[Name="Table12"]}[Content],
//set data types based on first entry in the column
//will be independent of the column names
typeIt = Table.TransformColumnTypes(Source,
List.Transform(
Table.ColumnNames(Source), each
{_,Value.Type(Table.Column(Source,_){0})})
),
//UNpivot except for the c.number and c.name columns to create the Parameter and Level columns
#"Unpivoted Other Columns" = Table.UnpivotOtherColumns(typeIt, {"C. number", "C. name"}, "Parameter", "Level"),
//Group By C.Number
//Add the appropriate rows for each customer
//And a blank row to separate the customers
#"Grouped Rows" = Table.Group(#"Unpivoted Other Columns", {"C. number"}, {
{"All", each _, type table [C. number=nullable number, C. name=nullable text, Parameter=text, Level=any]},
{"custLabel", (t)=> Table.InsertRows(t,0,{
[C. number = null, C. name=null,Parameter = null, Level = null],
[C. number = null, C. name=null, Parameter = "Customer Number", Level="Customer Name"],
[C. number = null, C. name=null,Parameter = t[C. number]{0}, Level = t[C. name]{0}],
[C. number = null, C. name=null,Parameter = "Parameter", Level = "Level"]
})}
}),
//Remove the unneeded columns and expand the remaining table
#"Removed Columns" = Table.RemoveColumns(#"Grouped Rows",{"C. number", "All"}),
#"Expanded custLabel" = Table.ExpandTableColumn(#"Removed Columns", "custLabel", {"Parameter", "Level"}, {"Parameter", "Level"}),
//Remove the top blank row
//promote the new blank row to the Header location
#"Removed Top Rows" = Table.Skip(#"Expanded custLabel",1),
#"Promoted Headers" = Table.PromoteHeaders(#"Removed Top Rows", [PromoteAllScalars=true]),
//data type set to text since it will look better on the report
#"Changed Type" = Table.TransformColumnTypes(#"Promoted Headers",{{"Customer Number", type text}, {"Customer Name", type text}})
in
#"Changed Type"```
Data
Results
[ Indirect with row() ]
Assuming 'SheetA'E3 column is the target and 'SheetB'Y2 is the source data.
In SheetA!E3 cell put:
=INDIRECT("SheetB!Y"&( ( (row()-3) / 12) + 2)
Press Enter
Then select SheetA!E3 cell, copy. Then paste in SheetA!E24. The formula will update itself.
Idea :
Find the relation between the target cell row number and the source cell row number. [ b > a : 3 > 2 , 15 > 3, 27 > 4 ] leads to a = (b-3)/12 + 2 . (The math is sort of like figuring out a straight line equation from 3 coordinate.) Then use INDIRECT() to combine the calculated row number with the column address.

Adding Columns using Power Queries in excel that copy the first row of the next column

I'm looking to create new columns to the left of columns 3 through 42 that simply copy the first value from the column to their right. In the picture, then, I'm looking for a column next to Column3 which just has "Auto" for every entry and then one to the left of Column4 that is full of "Home" and so on and so forth.
Current State
I got help to create queries that copy the value from Column3, "Auto", and then create a new column at the end of my data with that value, but I'm sure there's a way to make a loop that could simultaneously do this for every one of my columns (3-42) and maybe even put the columns where they're supposed to go (to the left of their matching column). Here is a photo of the queries I have thusfar:
current queries
Any help is greatly appreciated. I'm just learning how to use Power Query! Thanks so much.
This seems to work okay if you remove your existing
in
#"Added Custom"
and append this to your code in home ... advanced editor
First2Cols = List.FirstN(Table.ColumnNames(#"Added Custom"),2),
AdditionalCols = List.Range(Table.ColumnNames(#"Added Custom"),2),
TopRow = Table.RemoveColumns(Table.FirstN(#"Added Custom",1),First2Cols),
NewNames=List.Transform(AdditionalCols , each _ & "_"),
CombineTables = TopRow & Table.RenameColumns( #"Added Custom" , List.Zip( { AdditionalCols, NewNames} ) ),
#"Filled Down" = Table.Skip(Table.FillDown(CombineTables,AdditionalCols),1),
#"Reordered Columns" = Table.ReorderColumns(#"Filled Down", First2Cols & List.Sort( NewNames & AdditionalCols) )
in #"Reordered Columns"
you should then probably promote the first row to headers

Retrieving several matches from a string excel

Sorry if this is a stupid question but i've been racking my brain for a couple of days now and i can't seem to come up with a solution to this.
I have a list of phrases and a list of keywords that need to be searched, extracted and replaced.
For example i have the following list of keywords in sheet 1 column A that need to be extracted and replaced with the keywords in column B.
red - orange
blue - violet
green - pink
yellow - brown
And in sheet 2 I have a list of phrases in column A.
The girl with blue eyes had a red scarf.
I saw a yellow flower.
My cousin has a red car with blue rims and green mirrors.
And I want to extract in column B the keywords that are matched for every phrase in the exact order that they appear like so:
COLUMN A COLUMN B
The girl with blue eyes had a red scarf. violet, orange
I saw a yellow flower. brown
My cousin has a red car with blue rims and green mirrors. orange, violet, pink
Is there any way this can be achieved either by formula or VBA? Also this needs to be usable with Excel 2016 so i can't use fancy functions like "TEXTJOIN".
Thank you everyone in advance!
Cheers!
L.E.
I was able to find some code that almost does what I need it to do but it does not keep the correct order.
Is there anyway it could be modified to generate the desired results? Unfortunately I'm not that good with VBA. :(
Sub test()
Dim datacount As Long
Dim termcount As Long
datacount = Sheets("Sheet1").Cells(Rows.Count, "A").End(xlUp).Row
termcount = Sheets("Sheet2").Cells(Rows.Count, "A").End(xlUp).Row
For i = 1 To datacount
dataa = Sheets("Sheet1").Cells(i, "A").Text
result = ""
For j = 1 To termcount
terma = Sheets("Sheet2").Cells(j, "A").Text
termb = Sheets("Sheet2").Cells(j, "B").Text
If InStr(dataa, terma) > 0 Then
If result = "" Then
result = result & termb
Else
result = result & ", " & termb
End If
End If
Next j
Sheets("Sheet1").Cells(i, "B").Value = result
Next i
End Sub
You can do this with a User Defined Function making use of Regular Expressions.
The worksheet formula:
=matchWords(A2,$K$2:$L$5)
where A2 contains the sentence, and the second argument points to the translation table (which could be on another worksheet).
The code
Option Explicit
Function matchWords(ByVal s As String, translTbl As Range) As String
Dim RE As Object, MC As Object, M As Object
Dim AL As Object 'collect the replaced words
Dim TT As Variant
Dim I As Long
Dim vS As Variant
'create array
TT = translTbl
'initiate array for output
Set AL = CreateObject("system.collections.arraylist")
'initiate regular expression engine
Set RE = CreateObject("vbscript.regexp")
With RE
.Global = True
.ignorecase = True 'could change this if you want
.Pattern = "\w+" 'can change this if need to include some non letter/digit items
'split the sentence, excluding punctuation
If .test(s) Then
Set MC = .Execute(s)
For Each M In MC
For I = 1 To UBound(TT, 1)
If M = TT(I, 1) Then AL.Add TT(I, 2)
Next I
Next M
End If
End With
matchWords = Join(AL.toarray, ", ")
End Function
I would suggest you use Power Query which is a built-in function since Excel 2013.
Suppose the text strings of colours on your Sheet1 is in a Table named Tbl_LookUp
Suppose the phrases on your Sheet2 is in another Table named Tbl_Phrases
Go to the Data tab of your Excel and load both tables to the Power Query Editor (you can google how to load data from a table to the PQ Editor in Excel 2016). Please note the screenshot is from Excel 365.
Once loaded, go to the Tbl_Phrases query, and action the following steps:
Add an indexed column starting from 1
Split the Phrases column by delimiter, use space as the delimiter and choose to put the outcome into rows
Merge the current query with the Tbl_LookUp query, use the Phrase column to match the Old Text column
Expand the new column to show contents from New Text column
Group the New Text column by the Index column, you can choose to sum the values in the New Text column, and it will come up as an error after the grouping. Go to the formula field and replace this part of the formula List.Sum([New Text]) with Text.Combine([New Text],", "). Hit enter and the error will be corrected to the desired text string.
The following is the full M Code for the above query. You can copy and paste it in the Advanced Editor without manually going through each step:
let
Source = Excel.CurrentWorkbook(){[Name="Tbl_Phrases"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Phrases", type text}}),
#"Added Index" = Table.AddIndexColumn(#"Changed Type", "Index", 1, 1, Int64.Type),
#"Split Column by Delimiter" = Table.ExpandListColumn(Table.TransformColumns(#"Added Index", {{"Phrases", Splitter.SplitTextByDelimiter(" ", QuoteStyle.Csv), let itemType = (type nullable text) meta [Serialized.Text = true] in type {itemType}}}), "Phrases"),
#"Changed Type1" = Table.TransformColumnTypes(#"Split Column by Delimiter",{{"Phrases", type text}}),
#"Merged Queries" = Table.NestedJoin(#"Changed Type1", {"Phrases"}, Tbl_LookUp, {"Old Text"}, "Tbl_Replace", JoinKind.LeftOuter),
#"Expanded Tbl_Replace" = Table.ExpandTableColumn(#"Merged Queries", "Tbl_Replace", {"New Text"}, {"New Text"}),
#"Grouped Rows" = Table.Group(#"Expanded Tbl_Replace", {"Index"}, {{"Look up color", each Text.Combine([New Text],", "), type nullable text}})
in
#"Grouped Rows"
When you finish adding an index column in the Tbl_Phrases query, which is Step 1 from the above, you can make a copy of the query (simply right click the original query and select "duplicate"), then you will have a second query called Tbl_Phrases (2). No need to work on this query until you finish editing the original query ended up with desired text strings.
Then you can merge the Tbl_Phrases (2) query with the Tbl_Phrases query using the index column. Expand the new column to show the content from the look up colour column. Lastly, merge the Phrases column with the look up color column with delimiter (space)-(space), and remove the index column, then you should have the desired text string.
Here is the M Code for the Tbl_Phrases (2) query. Just a reminder, you must finish with the Tbl_Phrases query first otherwise the merging query step will lead to an error:
let
Source = Excel.CurrentWorkbook(){[Name="Tbl_Phrases"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Phrases", type text}}),
#"Added Index" = Table.AddIndexColumn(#"Changed Type", "Index", 1, 1, Int64.Type),
#"Merged Queries" = Table.NestedJoin(#"Added Index", {"Index"}, Tbl_Phrases, {"Index"}, "Tbl_Phrases", JoinKind.LeftOuter),
#"Expanded Tbl_Phrases" = Table.ExpandTableColumn(#"Merged Queries", "Tbl_Phrases", {"Look up color"}, {"Look up color"}),
#"Merged Columns" = Table.CombineColumns(#"Expanded Tbl_Phrases",{"Phrases", "Look up color"},Combiner.CombineTextByDelimiter(" - ", QuoteStyle.None),"Merged"),
#"Removed Columns" = Table.RemoveColumns(#"Merged Columns",{"Index"})
in
#"Removed Columns"
You can then load the Tbl_Phrase (2) query to the desired worksheet within the same workbook (or to somewhere on Sheet2).
Let me know if you have any questions.

Concatenate power query columns that are offset from each other

The problem
I have a data set with two header rows. I've transposed the rows into columns to work with the headers before combining, but I need help with concatenation of column1 into column2, since past row 7 the columns are offset from one another by one row (see example image).
The goal
I've tried to use replace and concatenate myself with an index, but have been unable to achieve the desired end result where column2 row 8 is concatenated with column1 row 7, so that when I combine these columns and transpose again the headers will be correctly labeled (see example image).
Thank you for any suggestions and your time.
Example image:
Here's one way.
I start with your Problem table as a table named Table1:
Then I add an index. (Add Column > Index Column):
Then I add a custom column. (Add Column > Custom Column) With this setup:
(#"Added Index"{[Index]-1}[Column1] references the entry in Column1 at the position record row that is equal to the value in the Index column, minus 1.)
...to get this:
Then I replaced Errors in the new Custom column. (Right-click Custom column title > click Replace Errors > type null > click OK)
Then I select Column1 and Custom column and remove other columns. (Select Column 1 column title > hold Ctrl and click Custom column title > keep holding Ctrl and right click Custom column title > click Remove Other Columns)
Here's my M code:
let
Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Column1", type text}, {"Column2", type text}}),
#"Added Index" = Table.AddIndexColumn(#"Changed Type", "Index", 0, 1),
#"Added Custom" = Table.AddColumn(#"Added Index", "Custom", each #"Added Index"{[Index]-1}[Column1]&"-"&[Column2]),
#"Replaced Errors" = Table.ReplaceErrorValues(#"Added Custom", {{"Custom", null}}),
#"Removed Other Columns" = Table.SelectColumns(#"Replaced Errors",{"Column1", "Custom"})
in
#"Removed Other Columns"
Another way.
Code:
let
Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
IndexedTable = Table.AddIndexColumn(Source, "Index", 0, 1),
Transform = Table.TransformRows(IndexedTable, (row)=>[Column1= row[Column1], Column2 = if row[Column1]=null then Text.Combine({IndexedTable{row[Index]-1}[Column1], "-",row[Column2]}) else row[Column2]]),
ToTable = Table.FromRecords(Transform)
in
ToTable
Brief explanation:
Source
Add index to address previous record
Use Table.TransformRows to analyze and transform each row to a record in this manner: Column1 taken from each row's column1 (row[Column1]), Column2 is generated from previous row using Text.Concatenate, IndexedTable{row[Index]-1}[Column1]. This yields value from previous row's Column1. Table.TransformRows returns list of records.
Transform list of records into the table.
This code will fail if 1st row contains null in [Column1]. If this is unacceptable, add another if-then-else.
Another way:
let
Source = Excel.CurrentWorkbook(){[Name="Table"]}[Content],
fillDown = Table.FillDown(Table.DuplicateColumn(Source, "Column1", "copy"),{"copy"}),
replace = Table.ReplaceValue(fillDown, each [Column2], each if [Column2] = null then null
else [copy]&"-"&[Column2], Replacer.ReplaceValue, {"Column2"})[[Column1],[Column2]]
in
replace

Resources