Group array values based on key in Lua? - algorithm

I have an array:
messages = {
{
id = 1,
sender = 987654321,
content = 'test message'
},
{
id = 2,
sender = 123456789,
content = 'testowa wiadomość'
},
{
id = 3,
sender = 123456789,
content = 'txd'
}
}
I want to group elements in array based on 'sender' as primary key. Final result should looks like that:
messages = {
{
sender = 987654321,
messages = {
'test message'
}
},
{
sender = 123456789,
messages = {
'testowa wiadomość',
'txd'
}
}
}
Could anyone help me out from this please?

OK, I solved this problem:
function groupMessages(array)
local result = {};
for k, v in ipairs(array) do
if not result[v.sender] then
result[v.sender] = {};
end
table.insert(result[v.sender], v);
end
return result;
end

Related

Matchstick: Assertion error: expected field equal '' but was ''

During testing my Graph Project I receive the following error:
nft-lottery
Player entity
𝖷 (assert.fieldEquals) Expected field 'lotteries' to equal '[0x1]', but was '[]' instead.
🛠 Mapping aborted at ~lib/matchstick-as/assembly/assert.ts, line 13, column 7, with message: Assertion Error
wasm backtrace:
0: 0x387c - <unknown>!~lib/matchstick-as/assembly/assert/assert.fieldEquals
1: 0x38fa - <unknown>!start:tests/nft-lottery.test~anonymous|0~anonymous|3
If I log the results after calling handleNftLotteryEnter() function I get (i.e., the lottery.id has not been stored even though it should have as it is derivedFrom the NftLottery - see below):
"Player": {
"0xdb...a7bf": {
"lotteries": {
"type": "List",
"data": []
}
}
},
...
If I try to mock firing three subsequent events, the first two players has got values stored but the last one has not, i.e.:
"Player": {
"0x5a...3ca1": {
"lotteries": {
"type": "List",
"data": [
{
"type": "String",
"data": "0x1"
}
]
}
},
"0xd1...6575": {
"lotteries": {
"type": "List",
"data": []
}
},
"0xdb...a7bf": {
"lotteries": {
"type": "List",
"data": [
{
"type": "String",
"data": "0x1"
}
]
}
}
},
What could be the reason thereof?
Thank you in advance!
Test file
import {
assert,
describe,
test,
clearStore,
beforeAll,
afterAll,
logStore,
} from "matchstick-as/assembly/index"
import { BigInt, Address } from "#graphprotocol/graph-ts"
import { handleNftLotteryEnter } from "../src/nft-lottery"
import { createNftLotteryEnterEvent } from "./nft-lottery-utils"
describe("Enter the NFT Lottery 1x", () => {
beforeAll(() => {
let lotteryCounter = BigInt.fromI32(1)
let player_one = Address.fromString("0xDB...a7bf")
let entranceFee = BigInt.fromI32(1)
let newNftLotteryEnterEvent = createNftLotteryEnterEvent(
lotteryCounter,
player_one,
entranceFee
)
handleNftLotteryEnter(newNftLotteryEnterEvent)
logStore()
})
afterAll(() => {
clearStore()
})
test("Player entity", () => {
assert.fieldEquals("Player", "0xdb...a7bf", "lotteries", "[0x1]")
})
})
describe("Enter the NFT Lottery 3x", () => {
beforeAll(() => {
let lotteryCounter = BigInt.fromI32(1)
let entranceFee = BigInt.fromI32(1)
let player_one = Address.fromString("0xDB...a7bf")
let player_two = Address.fromString("0x5a...3cA1")
let player_three = Address.fromString("0xd1...6575")
// the first event
let oneNftLotteryEnterEvent = createNftLotteryEnterEvent(
lotteryCounter,
player_one,
entranceFee
)
handleNftLotteryEnter(oneNftLotteryEnterEvent)
// the second event
let twoNftLotteryEnterEvent = createNftLotteryEnterEvent(
lotteryCounter,
player_two,
entranceFee
)
// the third event
handleNftLotteryEnter(twoNftLotteryEnterEvent)
let threeNftLotteryEnterEvent = createNftLotteryEnterEvent(
lotteryCounter,
player_three,
entranceFee
)
handleNftLotteryEnter(threeNftLotteryEnterEvent)
logStore()
})
afterAll(() => {
clearStore()
})
test("Counts", () => {
assert.entityCount("NftLottery", 1)
assert.entityCount("Player", 3)
})
test("Player Entity 3x", () => {
assert.fieldEquals("Player", "0xdb...a7bf", "lotteries", "[0x1]")
})
})
Schema.graphql
type NftLottery #entity {
id: ID!
openAtTimestamp: BigInt!
closeAtTimestamp: BigInt!
prize: BigInt!
players: [Player!]!
winners: [Player!]
requestId: BigInt
updatedAtTimestamp: BigInt
}
type Player #entity {
id: ID! # address
lotteries: [NftLottery!]! #derivedFrom(field: "players")
}
# events
type NftLotteryEnter #entity {
id: ID! # Set lotteryCounter + playerAddress
lotteryCounter: BigInt!
player: Bytes!
numberOfEntrances: [BigInt!]!
updatedAtTimestamp: BigInt
}
Handlers
import { BigInt, Address } from "#graphprotocol/graph-ts"
import {
NftLotteryEnter as NftLotteryEnterEvent
} from "../generated/NftLottery/NftLottery"
import { NftLottery, Player, NftLotteryEnter } from "../generated/schema"
export function handleNftLotteryEnter(event: NftLotteryEnterEvent): void {
/* Event params */
const playerId = event.params.player.toHexString()
const lotteryId = event.params.lotteryCounter.toHexString()
const entranceFee = event.params.entranceFee.toString()
const lotteryPlayerId = lotteryId + playerId
/* NftLottery: if a lottery does not exist, create it */
let nftLottery = NftLottery.load(lotteryId)
if (!nftLottery) {
nftLottery = new NftLottery(lotteryId)
nftLottery.openAtTimestamp = event.block.timestamp
nftLottery.closeAtTimestamp = BigInt.fromString("000000000000")
nftLottery.requestId = BigInt.fromString("0")
nftLottery.prize = BigInt.fromString("0")
nftLottery.players = new Array<string>()
nftLottery.winners = new Array<string>()
}
// update lottery data
let currentPrize = nftLottery.prize
nftLottery.prize = currentPrize.plus(BigInt.fromString(entranceFee))
// update players
let arrPlayers = nftLottery.players
arrPlayers.push(playerId)
nftLottery.players = arrPlayers
nftLottery.updatedAtTimestamp = event.block.timestamp
nftLottery.save()
/* Player: if a player does not exist, create them */
let player = Player.load(playerId)
if (!player) {
player = new Player(playerId)
player.save()
}
/* NftLotteryEnter */
let nftLotteryEnter = NftLotteryEnter.load((lotteryPlayerId))
if (!nftLotteryEnter) {
nftLotteryEnter = new NftLotteryEnter(lotteryPlayerId)
nftLotteryEnter.lotteryCounter = BigInt.fromString(event.params.lotteryCounter.toString())
nftLotteryEnter.player = Address.fromHexString(playerId)
nftLotteryEnter.numberOfEntrances = BigInt.fromString("0")
}
// update entrances
const entrances = nftLotteryEnter.numberOfEntrances
nftLotteryEnter.numberOfEntrances = entrances.plus(BigInt.fromString("1"))
nftLotteryEnter.save()
}
There might be a bug in Matchstick testing tool:
As PlayerEntity derives lotteries from NftLottery, the NftLottery should be saved AT THE END of all operations related to both entities.
Otherwise Player.lotteries does not get updated immediately and it stays empty, thus test fails.
...
// update when updated
nftLottery.updatedAtTimestamp = event.block.timestamp
/* Player: if a player does not exist, create them */
let player = Player.load(playerId)
if (!player) {
player = new Player(playerId)
player.save()
}
nftLottery.save()
...
Credit goes to Maks#3349 at unit testing of Graph discord.

local storage value gets overwritten

I have initiated 2 localstorage variables within a typescript function. The second variable holding the value overwrites the first variable. How do I fix it?
OnSelectedOppPropStatsChange(TypeId: string, StrSelectedValue: string): void {
this.appService.SetLoadingShow(true);
var url = this.configs.DashboardDemandStatsURL();
var Input = {
"TypeId": TypeId,
"PS_No": this.user.PS_No,
"StrSelectedValue": StrSelectedValue
};
localStorage.setItem(this.strTypeSelected, StrSelectedValue);
localStorage.setItem(this.strTypeIdValue, TypeId);
this.appService.GetDataFromAPIPost(url, Input)
.then(response => {
this.appService.SetLoadingShow(false);
if (response.ResponseCode == this.configs.RetCodeFailure()) {
this.ShowDemandDetails = false;
this.errorMessage = response.ResponseData;
this.appService.ShowMessagePopup(this.configs.MESSAGETYPEERROR(), this.errorMessage);
}
else {
this.OpenDemandStatsDetails = JSON.parse(response.ResponseData.strDemandStatsOpen);
this.TeamFulfilledStatsDetails = JSON.parse(response.ResponseData.strDemandStatsTeam);
this.RPMFulfilledStatsDetails = JSON.parse(response.ResponseData.strDemandStatsRPM);
this.TotalRRCount = this.OpenDemandStatsDetails.length + this.TeamFulfilledStatsDetails.length + this.RPMFulfilledStatsDetails.length;
}
},
error => { this.errorMessage = <string>error; this.appService.SetLoadingShow(false) });
}
OnClickOpenRRNavigate(): void {
let SelectedItem = localStorage.getItem(this.strTypeSelected);
let SelectedType = localStorage.getItem(this.strTypeIdValue);
this.appService.SetLoadingShow(true);
var url = this.configs.DashboardDemandStatsTableURL();
var Input = {
"TypeId": SelectedType,
"PS_No": this.user.PS_No,
"StrSelectedValue": SelectedItem,
"strRRAllocationValue": this.StrOpenValue
};
this.appService.GetDataFromAPIPost(url, Input)
.then(response => {
this.appService.SetLoadingShow(false);
if (response.ResponseCode == this.configs.RetCodeFailure()) {
this.errorMessage = response.ResponseData;
this.appService.ShowMessagePopup(this.configs.MESSAGETYPEERROR(), this.errorMessage);
}
else {
this.DemandTableDetails = JSON.parse(response.ResponseData.strDemandStatsTable);
this.ShowDemandTable = true;
}
},
error => { this.errorMessage = <string>error; this.appService.SetLoadingShow(false) });
}
In the function OnClickOpenRRNavigate() , the SelectedType and SelectedItem holds the same value. How can I fix it?

How to make ajax call on end of each block with infinite scrolling in ag-grid?

I am using ag-grid with angular 4.
I am using infinite scrolling as the rowModelType. But since my data is huge, we want to first call just 100 records in the first ajax call and when the scroll reaches the end, the next ajax call needs to be made with the next 100 records? How can i do this using ag-grid in angular 4.
This is my current code
table-component.ts
export class AssaysTableComponent implements OnInit{
//private rowData;
private gridApi;
private gridColumnApi;
private columnDefs;
private rowModelType;
private paginationPageSize;
private components;
private rowData: any[];
private cacheBlockSize;
private infiniteInitialRowCount;
allTableData : any[];
constructor(private http:HttpClient, private appServices:AppServices) {
this.columnDefs = [
{
headerName: "Date/Time",
field: "createdDate",
headerCheckboxSelection: true,
headerCheckboxSelectionFilteredOnly: true,
checkboxSelection: true,
width: 250,
cellRenderer: "loadingRenderer"
},
{headerName: 'Assay Name', field: 'assayName', width: 200},
{headerName: 'Samples', field: 'sampleCount', width: 100}
];
this.components = {
loadingRenderer: function(params) {
if (params.value !== undefined) {
return params.value;
} else {
return '<img src="../images/loading.gif">';
}
}
};
this.rowModelType = "infinite";
//this.paginationPageSize = 10;
this.cacheBlockSize = 10;
this.infiniteInitialRowCount = 1;
//this.rowData = this.appServices.assayData;
}
ngOnInit(){
}
onGridReady(params) {
this.gridApi = params.api;
this.gridColumnApi = params.columnApi;
//const allTableData:string[] = [];
//const apiCount = 0;
//apiCount++;
console.log("assayApiCall>>",this.appServices.assayApiCall);
const assaysObj = new Assays();
assaysObj.sortBy = 'CREATED_DATE';
assaysObj.sortOder = 'desc';
assaysObj.count = "50";
if(this.appServices.assayApiCall>0){
console.log("this.allTableData >> ",this.allTableData);
assaysObj.startEvalulationKey = {
}
}
this.appServices.downloadAssayFiles(assaysObj).subscribe(
(response) => {
if (response.length > 0) {
var dataSource = {
rowCount: null,
getRows: function (params) {
console.log("asking for " + params.startRow + " to " + params.endRow);
setTimeout(function () {
console.log("response>>",response);
if(this.allTableData == undefined){
this.allTableData = response;
}
else{
this.allTableData = this.allTableData.concat(response);
}
var rowsThisPage = response.slice(params.startRow, params.endRow);
var lastRow = -1;
if (response.length <= params.endRow) {
lastRow = response.length;
}
params.successCallback(rowsThisPage, lastRow);
}, 500);
}
}
params.api.setDatasource(dataSource);
this.appServices.setIsAssaysAvailable(true);
this.appServices.assayApiCall +=1;
}
else{
this.appServices.setIsAssaysAvailable(false)
}
}
)
}
}
I will need to call this.appServices.downloadAssayFiles(assaysObj) at the end of 100 rows again to get the next set of 100 rows.
Please suggest a method of doing this.
Edit 1:
private getRowData(startRow: number, endRow: number): Observable<any[]> {
var rowData =[];
const assaysObj = new Assays();
assaysObj.sortBy = 'CREATED_DATE';
assaysObj.sortOder = 'desc';
assaysObj.count = "10";
this.appServices.downloadAssayFiles(assaysObj).subscribe(
(response) => {
if (response.length > 0) {
console.log("response>>",response);
if(this.allTableData == undefined){
this.allTableData = response;
}
else{
rowData = response;
this.allTableData = this.allTableData.concat(response);
}
this.appServices.setIsAssaysAvailable(true);
}
else{
this.appServices.setIsAssaysAvailable(false)
}
console.log("rowdata>>",rowData);
});
return Observable.of(rowData);
}
onGridReady(params: any) {
console.log("onGridReady");
var dataSource = {
getRows: (params: IGetRowsParams) => {
this.info = "Getting datasource rows, start: " + params.startRow + ", end: " + params.endRow;
console.log(this.info);
this.getRowData(params.startRow, params.endRow)
.subscribe(data => params.successCallback(data));
}
};
params.api.setDatasource(dataSource);
}
Result 1 : The table is not loaded with the data. Also for some reason the service call this.appServices.downloadAssayFiles is being made thrice . Is there something wrong with my logic here.
There's an example of doing exactly this on the ag-grid site: https://www.ag-grid.com/javascript-grid-infinite-scrolling/.
How does your code currently act? It looks like you're modeling yours from the ag-grid docs page, but that you're getting all the data at once instead of getting only the chunks that you need.
Here's a stackblitz that I think does what you need. https://stackblitz.com/edit/ag-grid-infinite-scroll-example?file=src/app/app.component.ts
In general you want to make sure you have a service method that can retrieve just the correct chunk of your data. You seem to be setting the correct range of data to the grid in your code, but the issue is that you've already spent the effort of getting all of it.
Here's the relevant code from that stackblitz. getRowData is the service call that returns an observable of the records that the grid asks for. Then in your subscribe method for that observable, you supply that data to the grid.
private getRowData(startRow: number, endRow: number): Observable<any[]> {
// This is acting as a service call that will return just the
// data range that you're asking for. In your case, you'd probably
// call your http-based service which would also return an observable
// of your data.
var rowdata = [];
for (var i = startRow; i <= endRow; i++) {
rowdata.push({ one: "hello", two: "world", three: "Item " + i });
}
return Observable.of(rowdata);
}
onGridReady(params: any) {
console.log("onGridReady");
var datasource = {
getRows: (params: IGetRowsParams) => {
this.getRowData(params.startRow, params.endRow)
.subscribe(data => params.successCallback(data));
}
};
params.api.setDatasource(datasource);
}

How to map column to complex object in SlickGrid

var data = [{"Id":40072,"Id2":40071,"SmDetails":{"Id1":40071,"Id2":40072}}]
I want to display SmDetails.Id1 in a column. How is this possible? I tried:
var columns = [{name:'Personnel',field:SmDetails.id1,id:'detailId'}];
Please help me
Please help me
**My latest code**
var data = [{"Id":40072,"Id2":40071,"allocationDetails":{"Id1":40071,"allocationDetails":{"accommodationId":4007}}}]
var grid;
var columns = [ {name:"Personnel",field:"allocationDetails",fieldIdx:'accommodationId', id:"accommodationId"}];
var options = {
enableCellNavigation: true,
enableColumnReorder: false,
dataItemColumnValueExtractor:
function getValue(item, column) {
var values = item[column.field];
if (column.fieldIdx !== undefined) {
return values && values[column.fieldIdx];
} else {
return values;
}}};
var gridData=$scope.Vo;//This return as json format
grid = new Slick.Grid("#testGrid",gridData, columns);
This is the code tried recently.
You'll need to provide a custom value extractor to tell the grid how to read your object.
var options = {
enableCellNavigation: true,
enableColumnReorder: false,
dataItemColumnValueExtractor:
// Get the item column value using a custom 'fieldIdx' column param
function getValue(item, column) {
var values = item[column.field];
if (column.fieldIdx !== undefined) {
return values && values[column.fieldIdx];
} else {
return values;
}
}
};
The column definitions would look like:
{
id: "field1",
name: "Id1",
field: "SmDetails",
fieldIdx: 'Id1'
}, {
id: "field2",
name: "Id2",
field: "SmDetails",
fieldIdx: 'Id2'
} //... etc
Check out this fiddle for a working example.
try this to convert your data into object of single length values ...
newData = {};
for(key in data[0]){
parentKey = key;
if(typeof(data[0][key]) == "object"){
childData = data[0][key];
for(key in childData){
childKey = key;
newKey = parentKey+childKey;
newData[newKey] = childData[childKey];
}
} else {
newData[key] = data[0][key];
}
}
This will convert your data object like this
newData = {Id: 40072, Id2: 40071, SmDetailsId1: 40071, SmDetailsId2: 40072};
Now use this newData to map your data items in grid
I find this works well for nested properties, eg:
var columns = [
{ id: "someId", name: "Col Name", field: "myRowData.myObj.myProp", width: 40}
..
];
var options {
...
dataItemColumnValueExtractor: function getItemColumnValue(item, column) {
var val = undefined;
try {
val = eval("item." + column.field);
} catch(e) {
// ignore
}
return val;
}
};

How to project simple data set into to anonymous type of a different shape

I have a simple set of data that I am having trouble figuring out how to create the projection I want using LINQ.
public class Score {
public string Name { get; set; }
public int Value { get; set; }
}
var scores = new List<Score> {
new Score { Name = "jesse", Value = 10 },
new Score { Name = "jesse", Value = 12 },
new Score { Name = "jesse", Value = 15 },
new Score { Name = "billy", Value = 5 },
new Score { Name = "billy", Value = 7 },
new Score { Name = "billy", Value = 20 },
new Score { Name = "colin", Value = 25 },
new Score { Name = "colin", Value = 13 },
new Score { Name = "colin", Value = 8 }
};
I need to project 'scores' into an anonymous type with the following structure.
{
series : [
{ name : "jesse", data : [10, 12, 15 ] },
{ name : "billy", data : [ 5, 7, 20 ] },
{ name : "colin", data : [25, 13, 8 ] }
]
}
Any help is appreciated.
var result = new {
series = from score in scores
group score.Value by score.Name into nameValues
select new
{
name = nameValues.Key,
data = nameValues
}
};
Does this match the structure you want?
var query = scores.GroupBy(s => s.Name);
var result = query.Select(q => new {
Name = q.Key,
Data = q.ToArray().Select(k => k.Value)
});
var anotherAnon = new {series = result.ToArray()};

Resources