Related
I have a problem witn joining two tables (tbPlan and tbExecuted).
The tbPlan is like this
Date
SKU
Shift
Qty
1/1/2023
A
1
10
1/1/2023
A
2
5
1/1/2023
B
1
10
and the tbExecuted is like:
Date
SKU
Shift
Qty
1/1/2023
A
1
10
2/1/2023
A
1
5
1/1/2023
B
1
5
1/1/2023
B
2
5
The join table must have all the information for planning and executed like this:
Date
SKU
Shift
Plan
Executed
1/1/2023
A
1
10
10
1/1/2023
A
2
5
0
2/1/2023
A
1
0
5
1/1/2023
B
1
10
5
1/1/2023
B
2
0
5
I tried to use this DAX code (real names of tables and columns)
FACT-TabelaAderencia =
VAR A =
SELECTCOLUMNS(
summProducao
;"Data"; summProducao[DataProducao]+0
;"SKU"; Upper(summProducao[SKU])
;"Turno"; summProducao[Turno]*1
;"Produzido"; summProducao[Cargas Produzidas]
)
VAR B =
SELECTCOLUMNS(
summPlanejamento
;"Data"; summPlanejamento[DataProgramada1]+0
;"SKU"; UPPER(summPlanejamento[SKU])
;"Turno"; summPlanejamento[Turno]*1
;"Planejado"; summPlanejamento[Planejadas]
)
VAR Result = NATURALLEFTOUTERJOIN(A; B)
Return Result
The NATURALLEFTOUTERJOIN show only the 0s on the first argument of the function (if I put tbPlan, none row with 0 executed is shown). And vice-versa.
The NATURALINNERJOIN kills both rows with 0 in tbPlan or tbExecuted.
Is there a way to achieve this goal of having a summary of planning and executed in one summary table?
I appreciate any help.
NATURALLEFTOUTERJOIN is always a left join. So, you have left table and get it with some appended values.
You can't reach the goal with a join, but you can use some other technics. I used UNION() and GROUPBY(), but there are still other ways to get the same output.
VAR planFact =
UNION(
SELECTCOLUMNS(
plan
,"Data", plan[Date]+0
,"SKU", UPPER(plan[SKU])
,"Turno", plan[Shift]*1
,"Produzido",VALUE("0")
,"Planejado", plan[pQty]
)
,
SELECTCOLUMNS(
'fact'
,"Data", 'fact'[Date]+0
,"SKU", UPPER('fact'[SKU])
,"Turno", 'fact'[Shift]*1
,"Produzido", 'fact'[fQty]
,"Planejado",VALUE("0")
)
)
RETURN
GROUPBY(
planFact
,[Data]
,[SKU]
,[Turno]
,"Planejado",SUMX(CURRENTGROUP (),[Planejado])
,"Produzido",SUMX(CURRENTGROUP (),[Produzido])
)
Merging tables is really not something you should be doing using DAX. However, you can do this in Power Query with a full join and some null-coalesing operators. Here is some (really ugly) code:
tbPlan:
let
Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("i45WMtQ31DcyMDJW0lFyBGJDEDZQitXBkDECYlN0CSckLbEA", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [Date = _t, SKU = _t, Shift = _t, Qty = _t]),
#"Changed column type" = Table.TransformColumnTypes(Source, {{"Date", type date}, {"SKU", type text}, {"Shift", Int64.Type}, {"Qty", Int64.Type}})
in
#"Changed column type"
tbExecuted:
let
Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("i45WMtQ31DcyMDJW0lFyBGJDEDZQitWJVjLClDEFSyBpccInYQSRiAUA", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [Date = _t, SKU = _t, Shift = _t, Qty = _t]),
#"Changed column type" = Table.TransformColumnTypes(Source, {{"Date", type date}, {"SKU", type text}, {"Shift", Int64.Type}, {"Qty", Int64.Type}})
in
#"Changed column type"
And the merged table code:
let
Source = Table.NestedJoin(tbPlan, {"Date", "SKU", "Shift"}, tbExecuted, {"Date", "SKU", "Shift"}, "tbExecuted", JoinKind.FullOuter),
#"Expanded tbExecuted" = Table.ExpandTableColumn(Source, "tbExecuted", {"Date", "SKU", "Shift", "Qty"}, {"Date.1", "SKU.1", "Shift.1", "Qty.1"}),
#"Added custom" = Table.AddColumn(#"Expanded tbExecuted", "DateTemp", each [Date] ?? [Date.1]),
#"Added custom 1" = Table.AddColumn(#"Added custom", "SKUTemp", each [SKU] ?? [SKU.1]),
#"Added custom 2" = Table.AddColumn(#"Added custom 1", "ShiftTemp", each [Shift] ?? [Shift.1]),
#"Added custom 3" = Table.AddColumn(#"Added custom 2", "Qty Plan", each [Qty] ?? 0),
#"Added custom 4" = Table.AddColumn(#"Added custom 3", "Qty Executed", each [Qty.1] ?? 0),
#"Removed other columns" = Table.SelectColumns(#"Added custom 4", {"DateTemp", "SKUTemp", "ShiftTemp", "Qty Plan", "Qty Executed"}),
#"Renamed columns" = Table.RenameColumns(#"Removed other columns", {{"DateTemp", "Date"}, {"SKUTemp", "SKU"}, {"ShiftTemp", "Shift"}})
in
#"Renamed columns"
Result:
I have a code to insert all data from different excel files located in one folder as a loop ,
but the inserted data comes as a table (Vertically) , here is my question
what is the required modification to the existing code to make the inserted data transpose horizontally??????? thanks in advance
here is the pic of loaded data, i need these table to be shown horizontally
enter image description here
here is my code
Sub Basic_Example_1()
Dim MyPath As String, FilesInPath As String
Dim MyFiles() As String
Dim SourceRcount As Long, Fnum As Long
Dim mybook As Workbook, BaseWks As Worksheet
Dim sourceRange As Range, destrange As Range
Dim rnum As Long, CalcMode As Long
'Fill in the path\folder where the files are
MyPath = "C:\Users\mjamal\Desktop\Downloads\Sensor1"
'Add a slash at the end if the user forget it
If Right(MyPath, 1) <> "\" Then
MyPath = MyPath & "\"
End If
'If there are no Excel files in the folder exit the sub
FilesInPath = Dir(MyPath & "*.xl*")
If FilesInPath = "" Then
MsgBox "No files found"
Exit Sub
End If
'Fill the array(myFiles)with the list of Excel files in the folder
Fnum = 1
Do While FilesInPath <> ""
Fnum = Fnum + 1
ReDim Preserve MyFiles(1 To Fnum)
MyFiles(Fnum) = FilesInPath
FilesInPath = Dir()
Loop
'Change ScreenUpdating, Calculation and EnableEvents
With Application
CalcMode = .Calculation
.Calculation = xlCalculationManual
.ScreenUpdating = False
.EnableEvents = False
End With
Set BaseWks = Worksheets("Sheet1")
rnum = 5
'Loop through all files in the array(myFiles)
If Fnum > 0 Then
For Fnum = LBound(MyFiles) To UBound(MyFiles)
Set mybook = Nothing
On Error Resume Next
Set mybook = Workbooks.Open(MyPath & MyFiles(Fnum))
On Error GoTo 0
If Not mybook Is Nothing Then
On Error Resume Next
With mybook.Worksheets(1)
Set sourceRange = .Range("B1:BB7")
End With
If Err.Number > 0 Then
Err.Clear
Set sourceRange = Nothing
Else
'if SourceRange use all columns then skip this file
If sourceRange.Columns.Count >= BaseWks.Columns.Count Then
Set sourceRange = Nothing
End If
End If
On Error GoTo 0
If Not sourceRange Is Nothing Then
SourceRcount = sourceRange.Rows.Count
If rnum + SourceRcount >= BaseWks.Rows.Count Then
MsgBox "Sorry there are not enough rows in the sheet"
BaseWks.Columns.AutoFit
mybook.Close savechanges:=False
GoTo ExitTheSub
Else
'Copy the file name in column A
With sourceRange
BaseWks.Cells(rnum, "C"). _
Resize(.Rows.Count).Value = MyFiles(Fnum)
End With
'Set the destrange
Set destrange = BaseWks.Range("G" & rnum)
'we copy the values from the sourceRange to the destrange
With sourceRange
Set destrange = destrange. _
Resize(.Rows.Count, .Columns.Count)
End With
destrange.Value = sourceRange.Value
rnum = rnum + SourceRcount + 5
End If
End If
mybook.Close savechanges:=False
End If
Next Fnum
BaseWks.Columns.AutoFit
End If
ExitTheSub:
'Restore ScreenUpdating, Calculation and EnableEvents
With Application
.ScreenUpdating = True
.EnableEvents = True
.Calculation = CalcMode
End With
End Sub
For simplicity, file1.txt is a log file for which I extract logonIds into an array. File2.txt contains rows of logonID,emailAddress,other,needless,data
I need to take all of the logonIDs read into my array from file1 and extract their email addresses from file2. Once I have this information, I can then send each person in file1 an email. Can't just use file2.txt because it contains users who should not receive an email.
I wrote vbscript that extracts logonIDs from file1.txt into array and pulls logonID and email from file2.txt
inFile1 = "C:\Scripts\testvbs\wscreatestatus.txt"
inFile2 = "C:\Scripts\testvbs\WSBatchCreateBuildsList.txt"
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objInFile1 = objFSO.OpenTextFile(inFile1, ForReading)
Set objInFile2 = objFSO.OpenTextFile(inFile2, ForReading)
'Creates Array of all DomainIDs for successful deployments
Do Until objInFile1.AtEndOfStream
strNextLine = objInFile1.Readline
arrLogons = Split(strNextLine , vbTab)
If arrLogons(0) = "DEPLOYSUCCESS" Then
arrUserIDList = arrUserIDList & arrLogons(5) & vbCrLf
End If
Loop
Do Until objInFile2.AtEndOfStream
strNextLine = objInFile2.Readline
arrAddressList = Split(strNextLine , ",")
arrMailList = arrMailList & arrAddressList(0) & vbTab & arrAddressList(1) & vbCrLf
Loop
What I need to do next is take my list of user IDs "arrUserIDList", and extract their email address from arrMailList. With this information I can send each user in file1.txt (wscreatestatus.txt) an email.
Thanks!
From the way you construct your arrMailList, I presume you want the selected LogonID's and corresponding email addresses output to a new Tab delimited text file.
If that is the case, I recommend using ArrayList objects to store the values in. ArrayLists have an easy to use Add method and for testing if an item is in an ArrayList, there is the Contains method.
In Code:
Option Explicit
Const ForReading = 1
Const ForWriting = 2
Const ForAppending = 8
Dim inFile1, inFile2, outFile, objFSO, objInFile, objOutFile
Dim strNextLine, fields, arrUserIDList, arrMailList
inFile1 = "C:\Scripts\testvbs\wscreatestatus.txt"
inFile2 = "C:\Scripts\testvbs\WSBatchCreateBuildsList.txt"
outFile = "C:\Scripts\testvbs\WSEmailDeploySuccess.txt"
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objInFile = objFSO.OpenTextFile(inFile1, ForReading)
'Create an ArrayList of all DomainIDs for successful deployments
Set arrUserIDList = CreateObject( "System.Collections.ArrayList" )
Do Until objInFile.AtEndOfStream
strNextLine = objInFile.Readline
fields = Split(strNextLine , vbTab)
If fields(0) = "DEPLOYSUCCESS" Then
arrUserIDList.Add fields(5)
End If
Loop
'close the first input file
objInFile.Close
'Now read the second file and read the logonID's from it
Set objInFile = objFSO.OpenTextFile(inFile2, ForReading)
'Create an ArrayList of all LogonID's, a TAB character and the EmailAddress
Set arrMailList = CreateObject( "System.Collections.ArrayList" )
Do Until objInFile.AtEndOfStream
strNextLine = objInFile.Readline
fields = Split(strNextLine , ",")
If arrUserIDList.Contains(fields(0)) Then
' store the values in arrMailList as TAB separated values
arrMailList.Add fields(0) & vbTab & fields(1)
End If
Loop
'close the file and destroy the object
objInFile.Close
Set objInFile = Nothing
Set objOutFile = objFSO.OpenTextFile(outFile, ForWriting, True)
For Each strNextLine In arrMailList
objOutFile.WriteLine(strNextLine)
Next
'close the file and destroy the object
objOutFile.Close
Set objOutFile = Nothing
'clean up the other objects
Set objFSO = Nothing
Set arrUserIDList = Nothing
Set arrMailList = Nothing
Hope that helps
This is how I solved my problem, but I think Theo took a better approach.
Const ForReading = 1
Const ForWriting = 2
Dim inFile1, inFile2, strNextLine, arrServiceList, arrUserList
Dim arrUserDeployList, strLine, arrList, outFile, strItem1, strItem2
inFile1 = Wscript.Arguments.Item(0) 'wscreatestatus.txt file (tab delimited)
inFile2 = Wscript.Arguments.Item(1) 'WSBatchCreateBuildsList.txt (comma delimited)
outFile = Wscript.Arguments.Item(2) 'SuccessWSMailList###.txt (for Welcome emails)
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objInFile1 = objFSO.OpenTextFile(inFile1, ForReading)
Set objInFile2 = objFSO.OpenTextFile(inFile2, ForReading)
Set objOutFile = objFSO.CreateTextFile(outFile, ForWriting, True)
'Extracts Logon ID's for successfull deployments into an Array
'#================================================================
i = 0
Do Until objInFile1.AtEndOfStream
ReDim Preserve arrUsers(i)
strNextLine = objInFile1.Readline
arrLogons = Split(strNextLine , vbTab)
If arrLogons(0) = "PENDINGREQUESTS" Then
arrUsers(i) = arrLogons(5)
i = i + 1
End If
Loop
'Extracts success deploy email addresses and writes to file
'#================================================================
Do Until objInFile2.AtEndOfStream
ReDim Preserve arrMailList(i)
strNextLine = objInFile2.Readline
arrAddressList = Split(strNextLine , ",")
strItem1 = arrAddressList(0)
strItem2 = arrAddressList(1)
For Each strArrayEntry In arrUsers
If strArrayEntry = strItem1 Then
objOutFile.WriteLine strItem1 & "," & strItem2
End If
Next
i = i + 1
Loop
objOutFile.Close
objInFile1.Close
objInFile2.Close
I am trying to enumerate the paragraphs selected by the user in (Neo|Libre|Open)Office.
When I use the code below, modified version from here,
Sub CheckForSelection
Dim oDoc as Object
Dim oText
oDoc = ThisComponent
oText = oDoc.Text
if not IsAnythingSelected(oDoc) then
msgbox("No text selected!")
Exit Sub
end if
oSelections = oDoc.getCurrentSelection()
oSel = oSelections.getByIndex(0)
' Info box
'MsgBox oSel.getString(), 64, "Your Selection"
oPE = oSel.Text.createEnumeration()
nPars = 0
Do While oPE.hasMoreElements()
oPar = oPE.nextElement()
REM The returned paragraph will be a paragraph or a text table
If oPar.supportsService("com.sun.star.text.Paragraph") Then
nPars = nPars + 1
ElseIf oPar.supportsService("com.sun.star.text.TextTable") Then
nTables = nTables + 1
end if
Loop
' Info box
MsgBox "You selection has " & nPars & " paragraphs.", 64
end Sub
it finds ALL the paragraphs in the document, not just in the selection. Google has failed me. Any thoughts on how to find individual paragraphs in the selection?
The oSel.Text is a shortcut for oSel.getText() which "Returns the text interface in which the text position is contained." https://www.openoffice.org/api/docs/common/ref/com/sun/star/text/XTextRange.html#getText
So to get a ParagraphEnumeration only from the Selection, you should use oPE = oSel.createEnumeration() instead of oPE = oSel.Text.createEnumeration().
ORIGINAL POST
Given that there is no built in function in Lua, I am in search of a function that allows me to append tables together. I have googled quite a bit and have tried every solutions I stumbled across but none seem to work properly.
The scenario goes like this: I am using Lua embeded in an application. An internal command of the application returns a list of values in the form of a table.
What I am trying to do is call that command recursively in a loop and append the returned values, again in the form of a table, to the table from previous iterations.
EDIT
For those who come across this post in the future, please note what #gimf posted. Since Tables in Lua are as much like arrays than anything else (even in a list context), there is no real correct way to append one table to another. The closest concept is merging of tables. Please see the post, "Lua - merge tables?" for help in that regard.
Overcomplicated answers much?
Here is my implementation:
function TableConcat(t1,t2)
for i=1,#t2 do
t1[#t1+1] = t2[i]
end
return t1
end
If you want to concatenate an existing table to a new one, this is the most concise way to do it:
local t = {3, 4, 5}
local concatenation = {1, 2, table.unpack(t)}
Although I'm not sure how good this is performance-wise.
And one more way:
for _,v in ipairs(t2) do
table.insert(t1, v)
end
It seems to me the most readable one - it iterates over the 2nd table and appends its values to the 1st one, end of story. Curious how it fares in speed to the explicit indexing [] above
A simple way to do what you want:
local t1 = {1, 2, 3, 4, 5}
local t2 = {6, 7, 8, 9, 10}
local t3 = {unpack(t1)}
for I = 1,#t2 do
t3[#t1+I] = t2[I]
end
To add two tables together do this
ii=0
for i=#firsttable, #secondtable+#firsttable do
ii=ii+1
firsttable[i]=secondtable[ii]
end
use the first table as the variable you wanted to add as code adds the second one on to the end of the first table in order.
i is the start number of the table or list.
#secondtable+#firsttable is what to end at.
It starts at the end of the first table you want to add to, and ends at the end of the second table in a for loop so it works with any size table or list.
In general the notion of concatenating arbitrary tables does not make sense in Lua because a single key can only have one value.
There are special cases in which concatenation does make sense. One such is for tables containing simple arrays, which might be the natural result of a function intended to return a list of results.
In that case, you can write:
-- return a new array containing the concatenation of all of its
-- parameters. Scaler parameters are included in place, and array
-- parameters have their values shallow-copied to the final array.
-- Note that userdata and function values are treated as scalar.
function array_concat(...)
local t = {}
for n = 1,select("#",...) do
local arg = select(n,...)
if type(arg)=="table" then
for _,v in ipairs(arg) do
t[#t+1] = v
end
else
t[#t+1] = arg
end
end
return t
end
This is a shallow copy, and makes no attempt to find out if a userdata or function value is a container or object of some kind that might need different treatment.
An alternative implementation might modify the first argument rather than creating a new table. This would save the cost of copying, and make array_concat different from the .. operator on strings.
Edit: As observed in a comment by Joseph Kingry, I failed to properly extract the actual value of each argument from .... I also failed to return the merged table from the function at all. That's what I get for coding in the answer box and not testing the code at all.
If you want to merge two tables, but need a deep copy of the result table, for whatever reason, use the merge from another SO question on merging tables plus some deep copy code from lua-users.
(edit
Well, maybe you can edit your question to provide a minimal example... If you mean that a table
{ a = 1, b = 2 }
concatenated with another table
{ a = 5, b = 10 }
should result in
{ a = 1, b = 2, a = 5, b = 10 }
then you're out of luck. Keys are unique.
It seems you want to have a list of pairs, like { { a, 1 }, { b, 2 }, { a, 5 }, { b, 10 } }. You could also use a final structure like { a = { 1, 5 }, b = { 2, 10 } }, depending on your application.
But the simple of notion of "concatenating" tables does not make sense with Lua tables.
)
Here is an implementation I've done similar to RBerteig's above, but using the hidden parameter arg which is available when a function receives a variable number of arguments. Personally, I think this is more readable vs the select syntax.
function array_concat(...)
local t = {}
for i = 1, arg.n do
local array = arg[i]
if (type(array) == "table") then
for j = 1, #array do
t[#t+1] = array[j]
end
else
t[#t+1] = array
end
end
return t
end
Here is my implementation to concatenate a set of pure-integer-indexing tables, FYI.
define a function to concatenate two tables, concat_2tables
another recursive function concatenateTables: split the table list by unpack, and call concat_2tables to concatenate table1 and restTableList
t1 = {1, 2, 3}
t2 = {4, 5}
t3 = {6}
concat_2tables = function(table1, table2)
len = table.getn(table1)
for key, val in pairs(table2)do
table1[key+len] = val
end
return table1
end
concatenateTables = function( tableList )
if tableList==nil then
return nil
elseif table.getn(tableList) == 1 then
return tableList[1]
else
table1 = tableList[1]
restTableList = {unpack(tableList, 2)}
return concat_2tables(table1, concatenateTables(restTableList))
end
end
tt = {t1, t2, t3}
t = concatenateTables(tt)
-- Lua 5.1+
function TableAppend(t1, t2)
-- A numeric for loop is faster than pairs, but it only gets the sequential part of t2
for i = 1, #t2 do
t1[#t1 + 1] = t2[i] -- this is slightly faster than table.insert
end
-- This loop gets the non-sequential part (e.g. ['a'] = 1), if it exists
local k, v = next(t2, #t2 ~= 0 and #t2 or nil)
while k do
t1[k] = v -- if index k already exists in t1 then it will be overwritten
k, v = next(t2, k)
end
end
EDIT
Here's a better solution, the other one tended to overwrite numeric keys, the usage is still the same:
function merge(...)
local temp = {}
local index = 1
local result = {}
math.randomseed(os.time())
for i, tbl in ipairs({ ... }) do
for k, v in pairs(tbl) do
if type(k) == 'number' then
-- randomize numeric keys
k = math.random() * i * k
end
temp[k] = v
end
end
for k, v in pairs(temp) do
if type(k) == "number" then
-- Sort numeric keys into order
if result[index] then
index = index + 1
end
k = index
end
result[k] = v
end
return result
end
ORIGINAL
A wee bit late to the game, but this seems to work for me:
function concat(...)
local result = {}
for i, tbl in ipairs({...}) do
for k, v in pairs(tbl) do
if type(k) ~= "number" then
result[k] = v
else
result[i] = v
end
end
end
return result
end
It might be a bit overcomplicated, but it takes an infinite amount of arguments, and works for both key-value pairs and regular "arrays" (numbers as keys). Here's an example
I like the simplicity in #Weeve Ferrelaine answer, but mutations may cause many issues and in general, are not desirable.
Version with NO MUTATION.
---#param t1 {}
---#param t2 {}
function TableConcat(t1,t2)
local tOut = {}
for i = 1, #t1 do
tOut[i] = t1[i]
end
for i = #t1, #t1 + #t2 do
tOut[i] = t2[i]
end
return tOut
end
Original implementation, that's mutating t1.
function TableConcat(t1,t2)
for i=1,#t2 do
t1[#t1+1] = t2[i]
end
return t1
end
Use table.concat:
http://lua-users.org/wiki/TableLibraryTutorial
> = table.concat({ 1, 2, "three", 4, "five" })
12three4five
> = table.concat({ 1, 2, "three", 4, "five" }, ", ")
1, 2, three, 4, five
> = table.concat({ 1, 2, "three", 4, "five" }, ", ", 2)
2, three, 4, five
> = table.concat({ 1, 2, "three", 4, "five" }, ", ", 2, 4)
2, three, 4