Lua adding and filtering items in a table - filter

I am writing a WoW addon that will track and filter a list of items.
Items that are not already on the list are added; items that are already listed should not be added.
The issue I am getting is my check function does not consistently prevent duplicate items from being added to the list.
Adding item A the first time works fine, trying to re-add it again if it was the last thing added is correctly not re-added.
Adding item B the first time works fine, trying to re-add it again if it was the last thing added is correctly not re-added.
The problem is that if I try to re-add item A when it was not the last thing added it incorrectly re-adds the item to the list; so essentially I can re-add items as long as they were not the last item to be added.
Here is a gif that shows you what is happening;
And here are my two functions.
I have also linked my full lua code here and the toc here.
-- check if item is already listed
local function isItemOnList(incomingItemID)
for k, v in pairs(AAAGlobalItemLinkList) do
if v.itemID == incomingItemID then
print(v.itemID, ':matched:', incomingItemID)
return true
end
print('no match', incomingItemID)
return false
end
end
-- add item link to list function
local function addItemLinkToList()
local cursorItemType, cursorItemID, cursorItemLink = GetCursorInfo()
print(cursorItemID)
if not isItemOnList(cursorItemID) then
local itemName,
itemLink,
itemRarity,
itemLevel,
itemMinLevel,
itemType,
itemSubType,
itemStackCount,
itemEquipLoc,
iconFileDataID,
itemSellPrice,
itemClassID,
itemSubClassID,
bindType,
expacID,
itemSetID,
isCraftingReagent = GetItemInfo(cursorItemID)
tempItemList = {
itemID = cursorItemID,
itemName = itemName,
itemLink = itemLink,
itemRarity = itemRarity,
itemLevel = itemLevel,
itemMinLevel = itemMinLevel,
itemType = itemType,
itemSubType = itemSubType,
itemStackCount = itemStackCount,
itemEquipLoc = itemEquipLoc,
iconFileDataID = iconFileDataID,
itemSellPrice = itemSellPrice,
itemClassID = itemClassID,
itemSubClassID = itemSubClassID,
bindType = bindType,
expacID = expacID,
itemSetID = itemSetID,
isCraftingReagent = isCraftingReagent
}
table.insert(AAAGlobalItemLinkList, 1, tempItemList)
print(cursorItemLink, 'added to list')
else
print(cursorItemLink, 'already listed')
end
updateScrollFrame()
ClearCursor()
end
-- update scroll frames function
local function updateScrollFrame()
wipe(listItems)
for index = 1, #AAAGlobalItemLinkList do
--if testItemRarity(AAAGlobalItemLinkList[index]) then
table.insert(listItems, AAAGlobalItemLinkList[index]['itemLink'])
--end
end
FauxScrollFrame_Update(testingScrollFrame, #listItems, NumberOfButtons, HeightOfButtons)
for index = 1, NumberOfButtons do
local offset = index + FauxScrollFrame_GetOffset(testingScrollFrame)
local button = testingScrollFrame.buttons[index]
if index > #listItems then
button:SetText('')a
button.index = nil
else
button.index = offset
--local itemName, itemLink = GetItemInfo(listItems[offset])
button:SetText(listItems[offset])
end
end
end
I am not getting any errors at all.
I have also linked my full lua code here and the toc here.
Hopefully someone can explain how I have messed up and can also point me in the right direction to fix it.

in your function isItemOnList you return false inside for loop, so loop cant be executed for more than 1 iteration. You should put return false outside of for loop:
-- check if item is already listed
local function isItemOnList(incomingItemID)
for k, v in pairs(AAAGlobalItemLinkList) do
if v.itemID == incomingItemID then
print(v.itemID, ':matched:', incomingItemID)
return true
end
end
print('no match', incomingItemID)
return false
end
also you can do without return , sonil will be returned by default , and for if checks nil is the same as false

Related

office script - range find - return row or array to power automate

I have been trying several different ways to write an office script to search for a value in a cell and return the row or rows to power automate.
I believe I need to use range.find in order to make use of the "completematch: true" option.
However, I have also tried a filter and a foreach loop to find rows which include the text I am searching for.
I'm after a hint as to which method might be best?
essentially trying to:-
power automate - pass text parameter to the script
Scripts search for a match in excel business spreadsheet
the script finds match(s)
Script passes back the row(s) to powerautomate as an array
this is what I have so far: essentially it just finds the row number in which the matching result is found. This seems to work better to avoid partial matched (as happened with the filter method )
any pointers, most welcome
function main(workbook: ExcelScript.Workbook, siteNameToFilter: string) {
let activeSheet = workbook.getActiveWorksheet();
let range = activeSheet.getUsedRange();
let values = range.getValues();
/**
* This script searches for the next instance of the text "Fino" on the current worksheet.
*/
// Get the next cell that contains "Fino".
let findCell = range.find("Fino", {
completeMatch: true, /* Don't match if the cell text only contains "fino" as part of another string. */
matchCase: false,
searchDirection: ExcelScript.SearchDirection.forward /* Start at the beginning of the range and go to later columns and rows. */
});
// Set focus on the found cell.
findCell.select();
// Remove the "TK" text value from the cell, as well as any formatting that may have been added.
//tkCell.clear(ExcelScript.ClearApplyTo.all);
let row = findCell.getRow().getUsedRange();
let ur = findCell.getUsedRange();
console.log(row);
}
I think Find may only be returning the first match. It sounds like you want all matches with the siteName. To do this, you'd either want to filter the range or loop through it.
Here's an example that loops through the range and adds the values from the rows containing the site name to an array. After the loop's completed, the array containing the values is returning by the function:
function main(workbook: ExcelScript.Workbook, siteNameToFilter: string) {
let activeSheet = workbook.getActiveWorksheet();
let range = activeSheet.getUsedRange()
let values = range.getValues() as string[][];
let rowCount = range.getRowCount()
let colCount = range.getColumnCount()
let colIndex = range.getColumnIndex()
let rowsArr: string[][][] = []
for (let i = 0; i < rowCount; i++) {
for (let j = 0; j < colCount; j++) {
if (values[i][j] === siteNameToFilter) {
rowsArr.push(activeSheet.getRangeByIndexes(i, colIndex, 1, colCount).getValues() as string[][])
}
}
}
return rowsArr
}

How should I change his script so that it works for collections and not just one product in Shopify?

The script is a template that comes with the script editor app in Shopify. I need to make it work so that if you buy one product from a collection, you get another one free from that collection. This script works only for buying the same product. Here is the script:
PAID_ITEM_COUNT = 2
DISCOUNTED_ITEM_COUNT = 1
# Returns the integer amount of items that must be discounted next
# given the amount of items seen
#
def discounted_items_to_find(total_items_seen, discounted_items_seen)
Integer(total_items_seen / (PAID_ITEM_COUNT + DISCOUNTED_ITEM_COUNT) * DISCOUNTED_ITEM_COUNT) - discounted_items_seen
end
# Partitions the items and returns the items that are to be discounted.
#
# Arguments
# ---------
#
# * cart
# The cart to which split items will be added (typically Input.cart).
#
# * line_items
# The selected items that are applicable for the campaign.
#
def partition(cart, line_items)
# Sort the items by price from high to low
sorted_items = line_items.sort_by{|line_item| line_item.variant.price}.reverse
# Create an array of items to return
discounted_items = []
# Keep counters of items seen and discounted, to avoid having to recalculate on each iteration
total_items_seen = 0
discounted_items_seen = 0
# Loop over all the items and find those to be discounted
sorted_items.each do |line_item|
total_items_seen += line_item.quantity
# After incrementing total_items_seen, see if any items must be discounted
count = discounted_items_to_find(total_items_seen, discounted_items_seen)
# If there are none, skip to the next item
next if count <= 0
if count >= line_item.quantity
# If the full item quantity must be discounted, add it to the items to return
# and increment the count of discounted items
discounted_items.push(line_item)
discounted_items_seen += line_item.quantity
else
# If only part of the item must be discounted, split the item
discounted_item = line_item.split(take: count)
# Insert the newly-created item in the cart, right after the original item
position = cart.line_items.find_index(line_item)
cart.line_items.insert(position + 1, discounted_item)
# Add it to the list of items to return
discounted_items.push(discounted_item)
discounted_items_seen += discounted_item.quantity
end
end
# Return the items to be discounted
discounted_items
end
eligible_items = Input.cart.line_items.select do |line_item|
product = line_item.variant.product
!product.gift_card? && product.id == 11380899340
end
discounted_line_items = partition(Input.cart, eligible_items)
discounted_line_items.each do |line_item|
line_item.change_line_price(Money.zero, message: "Buy one Bolur, get one Bolur free")
end
Output.cart = Input.cart
I tried changing, what seems to be the relevant code:
eligible_items = Input.cart.line_items.select do |line_item|
product = line_item.variant.product
!product.gift_card? && product.id == 11380899340
end
to this:
eligible_items = Input.cart.line_items.select do |line_item|
product = line_item.variant.product
!product.gift_card? && **collection.id** == 123
end
but I get an error:
undefined method 'collection' for main (Your Cart)
undefined method'collection' for main (No Customer)
Two things here:
line_item.variant.product does not have the property collections. For that, you want to use line_item.product (docs) – which (should...see point two) expose all of the methods and properties of the product object.
However, in my attempt to do something similar to you (discount based on product) I tried iterating over line_item.variant – and am always hitting the error of undefined method 'product' for #<LineItem:0x7f9c97f9fff0>. Which I interpret as "line_items accessed in cart scripts can only be at the variant level".
So, I wonder if this is because the cart only contains variants (product/color/size) – so we aren't actually able to access the line_items by product, and only by variant.
I tired iterating over line_item.product_id, which also throws a similar error. I think we just have to try to do some hacky thing at the variant level.
I am going to see if I can access the product by the variant ID...back to the docs!
You actually can't do a collection, so you'd need to modify the script to work with a product type or tags. That script will need to be heavily modified to work for a number of products and not multiples of the same

duplicated rows in RDD

I encountered the following problem in spark:
...
while(...){
key = filtersIterator.next()
pricesOverLeadTimesKeyFiltered = pricesOverLeadTimesFilteredMap_cached
.filter(x => x._1.equals(key))
.values
resultSRB = processLinearBreakevens(pricesOverLeadTimesKeyFiltered)
resultsSRB = resultsSRB.union(resultSRB)
}
....
By this way, I accumulate the same resultSRB in resultsSRB.
But here are "some" tricks allowing me to add a different/right resultSRB for each iteration
call resultSRB.collect() or resultSRB.foreach(println) or println(resultSRB.count) after each processLinearBreakevens(..) call
perform the same operation on pricesOverLeadTimesKeyFiltered at the beginning of processLinearBreakevens(..)
It seems I need to ensure that all operations must be "flushed" before performing the union. I already tried the union through a temporary variable, or to persist resultSRB, or to persist pricesOverLeadTimesKeyFiltered but still the same problem.
Could you help me please?
Michael
If my assumption is correct; that all of these are var, then the problem is closures. key needs to be a val as it is being lazily captured into your filter. So, when it is finally acted on, the filtering is always using the last state of key
My example:
def process(filtered : RDD[Int]) = filtered.map(x=> x+1)
var i = 1
var key = 1
var filtered = sc.parallelize(List[Int]())
var result = sc.parallelize(List[Int]())
var results = sc.parallelize(List[Int]())
val cached = sc.parallelize(1 to 1000).map(x=>(x, x)).persist
while(i <= 3){
key = i * 10
val filtered = cached
.filter(x => x._1.equals(key))
.values
val result = process(filtered)
results = results.union(result)
i = i + 1
}
results.collect
//expect 11,21,31 but get 31, 31, 31
To fix it, change key to be val in the while loop and will get your expected 11,21,31

linq compare with previous item

I need to filter a list, by removing all items have the same language code as the item before (the items are orderd by time). This way, I want to detect all border crossings.
Is there a way to do this with one line of linq?
var test = new List<Sample> { new Sample("AT", "test1"),
new Sample("AT", "test2") ,
new Sample("AT", "test3") ,
new Sample("DE", "test4") ,
new Sample("DE", "test5") ,
new Sample("DE", "test6") ,
new Sample("AT", "test7") ,
new Sample("AT", "test8") ,
new Sample("AT", "test9")
};
var borderChanges = new List<Sample>();
var lastCountry = "";
foreach (var sample in test)
{
if (sample.country != lastCountry)
{
lastCountry = sample.country;
borderChanges.Add(sample);
}
}
I think this is what are you looking for:
test.Where((x,idx) => idx == 0 || x.Country != test[idx - 1].Country));
In case someone else is curious to see, how this problem can be solved with Aggregate(), here is the answer:
var borderChanges = test.Take(1).ToList();
test.Aggregate((last, curr) =>
{
if (last.Code != curr.Code)
{
borderChanges.Add(curr);
}
return curr;
});
Aggregate() executes the lambda expression for every element in the list, except the first. So we initialize the list with the first item.
In the lamda we add all items where the current item doesnt equal the aggregated value. In our case the aggredated value is always the previously checked item.
In the foreach iteration, access to neighboring elements of the sequence is impossible. Perhaps your goal can be achieved with the Aggregate extension method for IEnumerable<T>, where you can compare neighboring elements of the sequence.

How to check the selected Items

Using VB6
In the Form, i am having 2 list box name as lstDivison, lstDepartment
Code
For I = 0 To lstDivision.ListCount - 1
If lstDivision.Selected(I) = True Then
Filter = ""
Filter = lstDivision.List(I)
Divison
Else
ViewAll
End If
Next
For I = 0 To lstDepartment.ListCount - 1
If lstDepartment.Selected(I) = True Then
Filter = ""
Filter = lstDepartment.List(I)
Department
Else
ViewAll
End If
Next
Above code is working, but i want to know which listbox value is selected.
Conditon
If lstDivison list item is selected then it should not check the lstDepartment, if lstDepartment list item is selected then it should not check the lstDivison...
Code like this...
If lstDivison.selected = true then
some code
ElseIf lstDeartment.Selected = true then
some code
Else
Some code
End If
How to do this.
Need VB6 Code Help
One way to solve this is to ensure that only one of the listbox controls has a selected value at each time, by letting the listboxes clear the selection from the other listbox when selected. This makes it somewhat clearer to the user what values to expect from the filter, since there will only be selected values in one listbox at a time.
To do this, you can add this code:
private sub lstDepartment_Click()
For I = 0 to lstDivision.ListCount - 1
lstDivision.Selected(I) = False
Next
End Sub
private sub lstDivision_Click()
For I = 0 to lstDepartment.ListCount - 1
lstDepartment.Selected(I) = False
Next
End Sub
After this, your current code will work.

Resources