How to fetch a particular value from JSON if it exist - json

I have a json and want to fetch some values if it exists or not null.
From below JSON, I want to extract "options" of "questionGroup". Challenge is at some places "questionGroup" is empty.
"surveyQuestions": [
{
"questionTitle": "Enter your name",
"questionType": "Text",
"questionGroup": {}
},
{
"questionTitle": "Enter your age",
"questionType": "Number",
"questionGroup": {}
},
{
"questionTitle": "Select your gender",
"questionType": "Single choice",
"questionGroup": {
"options": [
{
"optionText": "Male"
},
{
"optionText": "Female"
}
],
"showRemarksBox": false
}
}
]

Just check whether the options are undefined or not.
// Iterate over survey questions
this.surveyQuestions.forEach( question => {
// Check if present or not
if(question.questionGroup.options!=undefined){
console.log(question.questionGroup.options);
return question.questionGroup.options;
}
});

You can use filter method for this.
let data = {"surveyQuestions": [
{
"questionTitle": "Enter your name",
"questionType": "Text",
"questionGroup": {}
},
{
"questionTitle": "Enter your age",
"questionType": "Number",
"questionGroup": {}
},
{
"questionTitle": "Select your gender",
"questionType": "Single choice",
"questionGroup": {
"options": [
{
"optionText": "Male"
},
{
"optionText": "Female"
}
],
"showRemarksBox": false
}
}
]}
let result = Array.from(data.surveyQuestions.filter(o => o.questionGroup.options), ({questionGroup}) => questionGroup.options);
console.log(result);

surveyQuestions.filter(s => s.questionGroup && Object.keys(s.questionGroup).length)

Related

How to search a JSON hash using a Regex?

I'm using an API to return all Macros to me, I am trying to return all the "macros" where "actions" contains a "value" matching my Regexp Pattern which I will link below.
I've tried below and other methods, but it returns me nil for present values. Any tips appreciated
macros["value"].select { |m| m['key'] == 'value' }.first['/^DE([0-9a-zA-Z]\s?){20}$/gm']
API result snippet:
jsObj
=> {"macros"=>
[{"url"=>"https://s/1900002708354.json",
"id"=>1900002708354,
"title"=>"Append Signature",
"active"=>true,
"updated_at"=>"2021-10-22T14:11:15Z",
"created_at"=>"2021-10-22T14:11:15Z",
"position"=>10001,
"description"=>"This macro appends a signature to the message ",
"actions"=>[{"field"=>"comment_value_html", "value"=>"<p>Mit besten Grüßen,</p><p>{{current_user.name}} [{{ticket.account}}] <br></p><p><br></p><p>{{dc.signature_email}}<br></p><p><br></p>"}],
"restriction"=>nil},
{"url"=>"949.json",
"id"=>59071949,
"title"=>"information",
"description"=>nil,
"actions"=>[{"field"=>"priority", "value"=>"low"}, {"field"=>"comment_value", "value"=>"DE89370400440532013000" "DE89 3704
0044 0532 0130 00"
"}],
"restriction"=>nil},
Desired Result:
{
"macros": [
{
"url": "x.json",
"id": 1900002708354,
"actions": [
{
"field": "comment_value_html",
"value": "DE89 3704 0044 0532 0130 00"
}
],
"restriction": null
},
{
"url": "x.json",
"id": 59071949,
"actions": [
{
"field": "priority",
"value": "low"
},
{
"field": "comment_value",
"value": "DE89 3704 0044 0532 0130 00
"
}
],
"restriction": null
},
Given that macros is the JSON object containing the macros data, you can use
macros.select { |m| m["actions"].any? { |w| /\ADE(?:[0-9a-zA-Z]\s?){20}\z/.match?(w["value"]) } }
Here is a Ruby demo:
require 'json'
j = <<-DATA
{
"macros": [
{
"url": "x.json",
"id": 1900002708354,
"actions": [
{
"field": "comment_value_html",
"value": "DE11111111111222222220"
}
],
"restriction": null
},
{
"url": "x.json",
"id": 59071949,
"actions": [
{
"field": "priority",
"value": "low"
},
{
"field": "comment_value",
"value": "DE12345678901234567890"
}
],
"restriction": null
}
]}
DATA
jsObj = JSON.parse(j)
macros = jsObj['macros']
puts jsObj['macros'].select { |m| m["actions"].any? { |w| /\ADE(?:[0-9a-zA-Z]\s?){20}\z/.match?(w["value"]) } }
Output:
{"url"=>"x.json", "id"=>1900002708354, "actions"=>[{"field"=>"comment_value_html", "value"=>"DE11111111111222222220"}], "restriction"=>nil}
{"url"=>"x.json", "id"=>59071949, "actions"=>[{"field"=>"priority", "value"=>"low"}, {"field"=>"comment_value", "value"=>"DE12345678901234567890"}], "restriction"=>nil}
The .select { |m| m["actions"].any? { |w| /\ADE(?:[0-9a-zA-Z]\s?){20}\z/.match?(w["value"]) } } main part gets all actions nodes that contain an array with a value key whose value matches the regex given.

Parse Complex JSON -- Map

I need to parse the complex JSON (below) IN SCALA to get the values of "expression" and "value" in "measure" key i.e I need List (COUNT, COUNT_DISTINCT ...) and List (1,tbl1.USER_ID ...).
I tried multiple options, but it is not working. Any help is appreciated
{
"uuid": "uuidddd",
"last_modified": 1559080222953,
"version": "2.6.1.0",
"name": "FULL_DAY_2_mand_date",
"is_draft": false,
"model_name": "FULL_DAY_1_may05",
"description": "",
"null_string": null,
"dimensions": [
{
"name": "PLATFORM",
"table": "tbl1",
"column": "PLATFORM",
"derived": null
},
{
"name": "OS_VERSION",
"table": "tbl1",
"column": "OS_VERSION",
"derived": null
}
],
"measures": [
{
"name": "_COUNT_",
"function": {
"expression": "COUNT",
"parameter": {
"type": "constant",
"value": "1"
},
"returntype": "bigint"
}
},
{
"name": "UU",
"function": {
"expression": "COUNT_DISTINCT",
"parameter": {
"type": "column",
"value": "tbl1.USER_ID"
},
"returntype": "hllc(12)"
}
},
{
"name": "CONT_SIZE",
"function": {
"expression": "SUM",
"parameter": {
"type": "column",
"value": "tbl1.SIZE"
},
"returntype": "bigint"
}
},
{
"name": "CONT_COUNT",
"function": {
"expression": "SUM",
"parameter": {
"type": "column",
"value": "tbl1.COUNT"
},
"returntype": "bigint"
}
}
],
"dictionaries": [],
"rowkey": {
"rowkey_columns": [
{
"column": "tbl1.OS_VERSION",
"encoding": "dict",
"encoding_version": 1,
"isShardBy": false
},
{
"column": "tbl1.PLATFORM",
"encoding": "dict",
"encoding_version": 1,
"isShardBy": false
},
{
"column": "tbl1.DEVICE_FAMILY",
"encoding": "dict",
"encoding_version": 1,
"isShardBy": false
}
]
},
"hbase_mapping": {
"column_family": [
{
"name": "F1",
"columns": [
{
"qualifier": "M",
"measure_refs": [
"_COUNT_",
"CONT_SIZE",
"CONT_COUNT"
]
}
]
},
{
"name": "F2",
"columns": [
{
"qualifier": "M",
"measure_refs": [
"UU"
]
}
]
}
]
},
"aggregation_groups": [
{
"includes": [
"tbl1.PLATFORM",
"tbl1.OS_VERSION"
],
"select_rule": {
"hierarchy_dims": [],
"mandatory_dims": [
"tbl1.DATE_HR"
],
"joint_dims": []
}
}
],
"signature": "ttrrs==",
"notify_list": [],
"status_need_notify": [
"ERROR",
"DISCARDED",
"SUCCEED"
],
"partition_date_start": 0,
"partition_date_end": 3153600000000,
"auto_merge_time_ranges": [
604800000,
2419200000
],
"volatile_range": 0,
"retention_range": 0,
"engine_type": 4,
"storage_type": 2,
"override_kylin_properties": {
"job.queuename": "root.production.P0",
"is-mandatory-only-valid": "true"
},
"cuboid_black_list": [],
"parent_forward": 3,
"mandatory_dimension_set_list": [],
"snapshot_table_desc_list": []
}
This is a snippet of the code I tried, and it is giving a null list
import org.json4s._
import org.json4s.jackson.JsonMethods._
implicit val formats = org.json4s.DefaultFormats
case class Function (
expression: String,
parameter: Parameter,
returntype: String
)
case class Parameter (
`type`: String,
value: String
)
case class Measures (
name: String,
function: Function
)
case class AllMeasuresData(uuid: String, measure: List[Measures])
val data = parse(tmp).extract[AllMeasuresData]
val names = data.measure.map(_.name)
println(names)
case class AllMeasuresData(uuid: String, measure: List[Measures])
val data = parse(tmp).extract[AllMeasuresData]
val names = data.measure.map(_.name)
println(names)
There are couple typos in your ADT:
Here is what you need:
case class Function (
expression: String,
parameter: Parameter,
returntype: String
)
case class Parameter (
`type`: String,
value: String
)
case class Measures (
name: String,
function: Function
)
case class AllMeasuresData(uuid: String, measures: List[Measures])
There is also an extra comma int the json, here is the correct:
{
"uuid":"uuidddd",
"last_modified":1559080222953,
"version":"2.6.1.0",
"name":"FULL_DAY_2_mand_date",
"is_draft":false,
"model_name":"FULL_DAY_1_may05",
"description":"",
"null_string":null,
"dimensions":[
{
"name":"PLATFORM",
"table":"tbl1",
"column":"PLATFORM",
"derived":null
},
{
"name":"OS_VERSION",
"table":"tbl1",
"column":"OS_VERSION",
"derived":null
} // There was an extra trailing comma here
],
"measures":[
{
"name":"_COUNT_",
"function":{
"expression":"COUNT",
"parameter":{
"type":"constant",
"value":"1"
},
"returntype":"bigint"
}
},
{
"name":"UU",
"function":{
"expression":"COUNT_DISTINCT",
"parameter":{
"type":"column",
"value":"tbl1.USER_ID"
},
"returntype":"hllc(12)"
}
},
{
"name":"CONT_SIZE",
"function":{
"expression":"SUM",
"parameter":{
"type":"column",
"value":"tbl1.SIZE"
},
"returntype":"bigint"
}
},
{
"name":"CONT_COUNT",
"function":{
"expression":"SUM",
"parameter":{
"type":"column",
"value":"tbl1.COUNT"
},
"returntype":"bigint"
}
}
],
"dictionaries":[
],
"rowkey":{
"rowkey_columns":[
{
"column":"tbl1.OS_VERSION",
"encoding":"dict",
"encoding_version":1,
"isShardBy":false
},
{
"column":"tbl1.PLATFORM",
"encoding":"dict",
"encoding_version":1,
"isShardBy":false
},
{
"column":"tbl1.DEVICE_FAMILY",
"encoding":"dict",
"encoding_version":1,
"isShardBy":false
}
]
},
"hbase_mapping":{
"column_family":[
{
"name":"F1",
"columns":[
{
"qualifier":"M",
"measure_refs":[
"_COUNT_",
"CONT_SIZE",
"CONT_COUNT"
]
}
]
},
{
"name":"F2",
"columns":[
{
"qualifier":"M",
"measure_refs":[
"UU"
]
}
]
}
]
},
"aggregation_groups":[
{
"includes":[
"tbl1.PLATFORM",
"tbl1.OS_VERSION"
],
"select_rule":{
"hierarchy_dims":[
],
"mandatory_dims":[
"tbl1.DATE_HR"
],
"joint_dims":[
]
}
}
],
"signature":"ttrrs==",
"notify_list":[
],
"status_need_notify":[
"ERROR",
"DISCARDED",
"SUCCEED"
],
"partition_date_start":0,
"partition_date_end":3153600000000,
"auto_merge_time_ranges":[
604800000,
2419200000
],
"volatile_range":0,
"retention_range":0,
"engine_type":4,
"storage_type":2,
"override_kylin_properties":{
"job.queuename":"root.production.P0",
"is-mandatory-only-valid":"true"
},
"cuboid_black_list":[
],
"parent_forward":3,
"mandatory_dimension_set_list":[
],
"snapshot_table_desc_list":[
]
}
Now you can run:
val data = parse(tmp).extract[AllMeasuresData]
val names = data.measures.map(_.name)
println(names)
// Displays
// List(_COUNT_, UU, CONT_SIZE, CONT_COUNT)
Your Parameter class does not match the JSON because you have used type1 rather than type as the field name. Use backticks to use "type" as a field name even though it is a reserved word:
case class Parameter (
`type`: String,
value: String
)
You also need to change the Function class as it has returntype1 rather than returntype:
case class Function (
expression: String,
parameter: Parameter,
returntype: String
)
The names of the fields in Scala must exactly match the names of the fields in the JSON. Extra fields in the JSON are ignored, but all the fields in the Scala must have matching fields in the JSON. If there are optional fields in the JSON then the Scala field type should be Option[...].

Set next step for the waterfall dialogue in Microsoft BotBuilder NodeJS SDK

I am using Microsoft Bot Framework for my facebook messenger bot. I want to load the dialog data from json files instead of hard coding in the js file. I would like to configure the next step in the dialog, based on result from the "current" step, which is part of the json file configuration, something like this.
{
"name": "welcome",
"type": "waterfall",
"steps": [
{
"id": 0,
"data": [
{
"type": "text",
"value": "Hey, It's nice to meet you."
},
{
"type": "quickReplies",
"value": "What do you want to do next?",
"options": [
{
"text": "some option 1",
"value": "option1"
},
{
"text": "some option 2",
"value": "option2"
}
]
}
],
"next": [
{
"result": "option1",
"action": "goto step 2"
},
{
"result": "option2",
"action": "goto step 5"
}
]
}
]
}
I would like to process all the incoming messages and respond with correct dialog or correct step in the dialog for the user.
I am trying something like this;
handleMessage = function (session) {
var step = session.dialogData["BotBuilder.Data.WaterfallStep"] || 0;
// check response data from previou step and identify the next step.
// set the waterfall step id
session.dialogData["BotBuilder.Data.WaterfallStep"] = 2;
session.send("Hello");
}
var bot = new builder.UniversalBot(connector, function (session) {
handleMessage(session);
})
.set('storage',tableStorage);
With this code, I am always getting step as zero for session.dialogData["BotBuilder.Data.WaterfallStep"] even after setting this to a different number.
Also, as soon as I set the waterfall step number, all other state data that is stored in my table storage for this conversation is gone.
Storage data before setting waterfall step:
{
"BotBuilder.Data.SessionState": {
"callstack": [
{
"id": "*:/",
"state": {
"BotBuilder.Data.WaterfallStep": 0
}
},
{
"id": "*:welcome",
"state": {
"BotBuilder.Data.WaterfallStep": 1
}
},
{
"id": "BotBuilder:prompt-text",
"state": {
"options": {
"prompt": {
"type": "message",
"agent": "botbuilder",
"source": "facebook",
"address": {
"id": "mid.$cAAAlr-0LRH9niO21L1hV6hs83GuJ",
"channelId": "facebook",
"user": {
"id": "XXXX",
"name": "XXXX"
},
"conversation": {
"isGroup": false,
"id": "XX"
},
"bot": {
"id": "XXX",
"name": "XXX"
},
"serviceUrl": "https://facebook.botframework.com"
},
"text": "what do you want to next"
//ignored for simplicity
},
"promptAfterAction": true,
"libraryNamespace": "*"
},
"turns": 0,
"lastTurn": 1517594116372,
"isReprompt": false
}
}
],
"lastAccess": 1517594112740,
"version": 0
}
}
After I set the waterfall step:
{
"BotBuilder.Data.SessionState": {
"callstack": [
{
"id": "*:/",
"state": {
"BotBuilder.Data.WaterfallStep": 2
}
}
],
"lastAccess": 1517602122416,
"version": 0
}
}
Interestingly the step number is saved to the database (but in session state) but my "session" variable do not have this value anywhere. Also, even after configuring custom state service, the serviceUrl is still https://facebook.botframework.com which I thought is the default state service used if there is no state service set for the bot.
Per your code, as your bot actually contains only one waterfall step: handleMessage(session);, which raised your issue. You can consider to create multiple dialogs from json configration instead of complex waterfall steps.
Here is my quick test, for your information:
const json = `
[{
"name": "welcome",
"type": "waterfall",
"steps": [
{
"id": 0,
"data": [
{
"type": "text",
"value": "Hey, It's nice to meet you."
},
{
"type": "quickReplies",
"value": "What do you want to do next?",
"options": [
{
"text": "some option 1",
"value": "option1"
},
{
"text": "some option 2",
"value": "option2"
}
]
}
],
"next": [
{
"result": "option1",
"action": "dialog2"
},
{
"result": "option2",
"action": "dialog3"
}
]
}
]
},{
"name":"dialog2",
"type": "waterfall",
"steps": [
{
"data": [
{
"type": "text",
"value": "Hey, this is dialig2."
}]
}
]
},{
"name":"dialog3",
"type": "waterfall",
"steps": [
{
"data": [
{
"type": "text",
"value": "Hey, this is dialig3."
}]
}
]
}]
`;
const generateSignleStep = (step) => {
return (session, args, next) => {
step.forEach(sentence => {
switch (sentence.type) {
case 'quickReplies':
let choices = sentence.options.map(item => {
return item.value
});
let card = new builder.ThumbnailCard(session)
.text(sentence.value)
.buttons(sentence.options.map(choice => new builder.CardAction.imBack(session, choice.value, choice.text)))
let message = new builder.Message(session).addAttachment(card);
builder.Prompts.choice(session, message, choices);
break;
case 'text':
default:
session.send(sentence.value)
break;
}
})
}
}
const generatenextAction = (actions) => {
return (session, args, next) => {
const response = args.response;
actions.map(action => {
if (action.result == response.entity) {
session.beginDialog(action.action);
}
})
}
}
const generateWaterfallSteps = (steps) => {
let waterfall = [];
steps.forEach(step => {
waterfall.push(generateSignleStep(step.data));
if (step.next) {
waterfall.push(generatenextAction(step.next));
}
});
return waterfall;
}
var bot = new builder.UniversalBot(connector);
const jsonobj = JSON.parse(json);
jsonobj.forEach(dialog => {
bot.dialog(dialog.name, generateWaterfallSteps(dialog.steps))
.triggerAction({
matches: new RegExp(dialog.name, "g")
})
});
The result is:

Any smart JSON/JS Object counting packages?

I don't mean to be duplicating any questions, since I have read so many great answers here on StackOverflow.
Given the below JSON data:
[
{
“department”: “vice”,
“team”: [
{
"selected": "Yes"
},
{
“selected": “No”
}
],
“fund”: [
“team_a”
“team_c”
]
}
]
I want to return a count of selected, so from the above 'yes'=1 and 'no'=1.
I understand I can do it through for loop, using a simple countYes++ to return the answer, however I have 2 questions as below:
Is there any other way (i.e. using some npm package).
In my example the options are simple Yes and No, if I have a bigger list of things to count, how to count all unique ones > 0?
Thanks.
I tried to write using vanilla JS you can look for library like underscore, lodash for more options.
var data = [
{
"department": "vice",
"team": [
{
"selected": "Yes"
},
{
"selected": "No"
}
],
"fund": [
"team_a",
"team_c"
]
},
{
"department": "vice",
"team": [
{
"selected": "Yes"
},
{
"selected": "Yes"
}
],
"fund": [
"team_a",
"team_c"
]
},
{
"department": "vice",
"team": [
{
"selected": "Yes"
},
{
"selected": "No"
}
],
"fund": [
"team_a",
"team_c"
]
}
];
function decisionReducer(prev, next) {
var team = next.team;
if (team && Array.isArray(team)) {
team.forEach(function (row) {
if (row.selected === 'Yes') {
prev.yes += 1;
}
if (row.selected === 'No') {
prev.no += 1;
}
})
}
return prev;
};
var counter = data.reduce(decisionReducer, {yes: 0, no: 0})
console.log(counter);

jqgrid - json - getting wrong value

I have the following JSON:
{
"wrapper": {
"table": {
"rows": [
{
"cells": [
{
"fname": "Jack",
"label": "fname",
"editable": false
},
{
"lname": "Kalra",
"label": "lname",
"editable": true,
"details": [
{
"industry": "music"
},
{
"industry": "media"
}
]
}
]
},
{
"cells": [
{
"fname": "Steven",
"editable": true,
"label": "fname"
},
{
"lname": "Martini",
"editable": true,
"label": "lname"
}
]
},
{
"cells": [
{
"fname": "Andrea",
"editable": true,
"label": "fname"
},
{
"lname": "Dmello",
"editable": true,
"label": "lname",
"details": [
{
"industry": "finance"
},
{
"industry": "HR"
},
{
"industry": "media"
}
]
}
]
},
{
"cells": [
{
"fname": "James",
"label": "fname",
"editable": false
},
{
"label": "lname",
"lname": "Diamond",
"editable": true,
"details": [
{
"industry": "music"
},
{
"industry": "media"
}
]
}
]
},
{
"cells": [
{
"fname": "Aba",
"label": "fname",
"editable": true
},
{
"label": "lanme",
"lname": "Duck",
"editable": true,
"details": [
{
"industry": "finance"
},
{
"industry": "technology"
},
{
"industry": "media"
}
]
}
]
},
{
"cells": [
{
"fname": "Henry",
"label": "fname",
"editable": true
},
{
"label": "lname",
"lname": "Hebert",
"editable": true,
"details": [
{
"industry": "finance"
},
{
"industry": "HR"
},
{
"industry": "media"
},
{
"industry": "IT"
}
]
}
]
}
]
}
}
}
All cells are editable.
I'm trying to loop through each row and then each cell to find out the number of properties in "details". In inline editing mode, it should be a text field and the value of text field should be the corresponding number of properties.
Example - for Marc Kalra, the details cell will be a text field with a value of 2.
Here's my code
loadComplete: function(data){
var x, y, cellProp;
for (x = 0; x < data.wrapper.table.rows.length; x++) {
var cellCount = data.wrapper.table.rows[x].cells.length;
for (y = 0; y < cellCount; y += 1) {
cellProp = data.wrapper.table.rows[x].cells[y];
var prop, listCount, cellLabel;
listCount = data.wrapper.table.rows[x].cells[y].details.length;
cellLabel = data.wrapper.table.rows[x].cells[y].label;
function gridCustomEdit (value, options){
var el = document.createElement("input");
el.type="text";
el.value = listCount;
return el;
}
for (prop in cellProp) { if (prop === "details"){
$("#jqgrid").setColProp(cellLabel, {
editable: true,
edittype: "custom",
editoptions: {
custom_element: gridCustomEdit
}
});
}
}
}
}
PROBLEM - is that el.value = listCount; in the above code always returns 4 as the number of properties for each row/cell.
Can someone point me my mistake?
UPDATED
loadComplete: function(data){
var x, y, cellProp;
for (x = 0; x < data.wrapper.table.rows.length; x++) {
var cellCount = data.wrapper.table.rows[x].cells.length;
for (y = 0; y < cellCount; y += 1) {
cellProp = data.wrapper.table.rows[x].cells[y];
var isEditable = cellProp.editable;
if (isEditable === false) {
$("#jqgrid").setColProp(cellProp.label, {
editable: false
});
}else {
var listCount, cellLabel;
listCount = data.wrapper.table.rows[x].cells[y].details.length;
cellLabel = data.form.summary.rows[x].cells[y].label;
$("#jqgrid").setColProp(cellLabel, {
editable: true,
editoptions: {
dataInit: function(elem){
$(elem).myPlugin({listCount: listCount})
}
}
})
}
}
}
}
PLUGIN CODE
(function( $ ){
$.fn.extend({
myPlugin: function(options){
var defaults = {
listCount: 0
};
var options = $.extend(defaults, options);
var o = options;
alert(o.listCount);
if (o.listCount === 3){
$(elem).html("<input type='text' value='" + o.listCount + "'>")
} else {
$(elem).html("<select>") **// this should be a dropdown with values = properties of `details`**
}
}
})
})
GRID CODE
$("#jqgrid").jqGrid({
datatype: "json",
colNames:['fname','lname'],
colModel:[
{name:'fname',index:'fname'},
{name:'lname',index:'lname'},
],
jsonReader: {
root: "wrapper.table.rows",
repeatitems: false
},
onSelectRow: function(id){
$(this).jqGrid('editRow',id);
},
})
If details exist + number of properties in details = 3, then lname is displayed as a text field in inline editing mode.
If details exist + number of properties in details > 3, then lname is displayed as a select field in inline editing mode.
The method setColProp set property for the column and not for the cell in the column. So if you set in the loop setColProp for the same column many times only the last setting will be applied. Because the last row (for "Love Hebert") in your data has 4 items in the details array only the value will be used.
The next error is that you define gridCustomEdit function which has reference to external variable listCount. In the case there are only one instance of the function with the last value of the variable. If you need to have many function instances with different values you should use closure.
In your case it seems to me you can implement all what you need even without the usage of closure and without custom editing (edittype:'custom' with custom_element and custom_value).
I suppose that all what you need to do is to set setColProp inside of onSelectRow (or before the call of editRow) and not inside of loadComplete. See the answer for more information. If you need to change the edittype of the column you can do this in the same way. So you can for example dynamically set edittype: "text" and set editoptions with dataInit which change the value of the element.
If it is needed you can even dynamically switch the edittype between edittype: "text" and edittype: "select" and set all editoptions which are required.
In the way you will receive simple and flexible code which don't use custom editing at all.