I have a structure of dynamic DOM elements that I'm continuously evaluating with a for loop and a setInterval. While in that interval I sort the array and then change the dom with a JQuery Sort function in order to order my DOM structure on the fly.
The thing is I only want to change the DOM if the sort function is actually changing the order of elements (usually this happens when a new DOM element comes into the structure dynamically), if the sort function runs again over an already sortered array I don't want to do anything with the dom.
I have found A LOT of ways to compare if Arrays are equal but my case is special since i don't really care about the arrays elements / content but I really need to check if those elements are not in the same order so i can change the dom.
setInterval(function () {
//Incoming Chat Structure Variables
var $incomingChatsTable, $incomingChatsRows, $incomingChatTags
$incomingChatsTable= $("div[__jx__id*='incoming_list__list_content_container']")
$incomingChatsRowsContainer=$("div[__jx__id='___$_194__visitor_list__incoming_list__list__content']")
$incomingChatsRows = $("div[class*='renderers_Incoming']")
$incomingChatTags = $( "div[__jx__id*='incoming_list__list__content'] div[class*='renderers_Incoming'] .visitorlist_tags .jx_ui_html_span:last-child .tag").toArray()
$popupTrigger = $("div[jx\\:list\\:rowid]")
//Assigning Priorities Function
function assignPriority(arrayParent) {
for ( var i=0; i <= arrayParent.length; i++) {
var actualRow = $(arrayParent[i])
var minutesInQueue = $(actualRow).find('.time_cell').text().substr(0,2)
var priorityVal = parseInt(minutesInQueue)
var actualRowTags = $( actualRow ).find('.tag').toArray()
$(actualRow).addClass('priorityRow')
for (var k=0; k <= actualRowTags.length; k++) {
let normalHolderTag = $(actualRowTags[k]).text().toLowerCase()
if (normalHolderTag === 'vip') {
priorityVal += 30
$(actualRow).attr('data-priority', priorityVal)
} else if ( normalHolderTag === 'rg' ) {
priorityVal += 20
$(actualRow).attr('data-priority', priorityVal)
} else if ( normalHolderTag === 'accountclosure' ) {
priorityVal += 10
$(actualRow).attr('data-priority', priorityVal)
} else {
$(actualRow).attr('data-priority', priorityVal)
}
//$(actualRow).find('.numbers_cell').text(priorityVal)
}
}
}
//Sorting Incoming Chats By Priority
function orderByPriority(container) {
myArray = container.find('.priorityRow')
var changed = false
myArray.sort(function(a,b){
contentA = parseInt( $(a).data('priority') )
contentB = parseInt( $(b).data('priority') )
if ( (contentB - contentA) * ( myArray.indexOf(b) - myArray.indexOf(a) ) > 0 ){
changed = true
}
return (contentB - contentA)
})
if(changed) {
$(sortedArray).prependTo( container )
} else{
console.log('No need To Sort')
}
}
setInterval(function () {
assignPriority( $incomingChatsRows )
orderByPriority( $incomingChatsRowsContainer )
}, 500)
}, 1000)
UPDATE:
myArray is now a global variable, I have added an IF statement evaluating the sort function result and multiplying that result by the a, b indexes. If that's greater than 0 then i change the value of my "changed" variable to "true" BUT I'm getting a "myArray.indexOf is not a function" error.
You can add a line of code in your orderByPriority sort function which sets a variable to true only if it actually changes something:
function orderByPriority(container) {
var myArray = container.find('.priorityRow')
var changed = false
myArray.sort(function(a,b){
contentA = parseInt( $(a).data('priority') )
contentB = parseInt( $(b).data('priority') )
if((contentB - contentA)*(myArray.index(b)-myArray.index(a))>0){
//check if these have the same sign, i.e. something has changed
changed = true;
}
return (contentB - contentA)
})
if(changed){}//do something to modify DOM
}
The sort function has three possible outputs which will determine whether the two inputs need to be sorted:
Greater than 0: sort a to an index greater than b
Less than 0: sort b to an index greater than a
Equal to 0: don't change the order
The possible conditions which will result in a change being made are therefore:
Output greater than 0 and the index of a is currently less than the index of b
Output less than 0 and the index of a is currently greater than the index of b
We can represent the difference between the two indexes as index(b)-index(a). If the index of a is currently less than the index of b, this value will be positive. Otherwise, the value will be negative.
We can thus see that the above conditions are really saying:
if output and index(b)-index(a) have the same sign, something needs to be changed.
We can check if they have the same sign by multiplying them by eachother and testing if the result is greater than 0. This means that something has changed, so inside the if condition we set changed to true
Here is what I have tried.
I have tried dot notation and quotes. None of them seem to work. What exactly could be the problem?
var clientsList;
Client.find({}, function(err, clients) {
clientsList = clients;
// I have 10 clients in the loop
for (var j = 0; j < clientsList.length; j++) {
var x = clientsList[j];
x.count = "20";
//x["count"] = "20";
console.log(x);
}
});
Existing Client object
{"name":"abcd", "email":"abc#gmail.com"}
I'm unable to add the count key value pair into the client object. What could be the problem?
I suspect the object you're being given by Client.find has extensions prevented (it's sealed or frozen).
You can create a new object with the original's own, enumerable properties plus your count property using ES2018 spread notation:
x = {...x, count: "20"};
...or ES2015's Object.assign:
x = Object.assign({}, x, {count: "20"});
If the array is also sealed or frozen, you can copy it and its objects like this:
clientList = clients.map(client => {
return {...client, count: "20"}; // Or with `Object.assign`
});
or even with the concise form of the arrow function:
clientList = clients.map(client => ({...client, count: "20"}));
Side note: This pattern:
var clientsList;
Client.find({}, function(err, clients) {
clientsList = clients;
// ...
});
...often suggests that you intend to use clientsList in code following the Client.find call and expect it to have the result from that call's callback. See this question's answers for why, if you're doing that, it doesn't work.
I have a map function that returns a result like this :
{"total_rows":100995,"rows":[
{"id":"00001_372792","key":["00001","CADENCIER",0],"value":-0.1961035657664066},
{"id":"00001_372792","key":["00001","CADENCIER",0],"value":-0.1961035657664066},
{"id":"00001_386302","key":["00001","CADENCIER",0],"value":0.6934708647727543},
{"id":"00001_386302","key":["00001","CADENCIER",0],"value":0.6934708647727543},
{"id":"00001_386963","key":["00001","CADENCIER",0],"value":0.6922628824612621},
{"id":"00001_386963","key":["00001","CADENCIER",0],"value":0.6922628824612621},
{"id":"00001_387089","key":["00001","CADENCIER",0],"value":0.6919048724571887},
{"id":"00001_387089","key":["00001","CADENCIER",0],"value":0.6919048724571887},
{"id":"00001_387091","key":["00001","CADENCIER",0],"value":0.6919048724571887},
{"id":"00001_387091","key":["00001","CADENCIER",0],"value":0.6919048724571887},
{"id":"00001_387099","key":["00001","CADENCIER",0],"value":0.6921140124188077},
{"id":"00001_387099","key":["00001","CADENCIER",0],"value":0.6921140124188077},
{"id":"00001_387105","key":["00001","CADENCIER",0],"value":0.6921140124188077},
{"id":"00001_387105","key":["00001","CADENCIER",0],"value":0.6921140124188077},
{"id":"00001_387193","key":["00001","CADENCIER",0],"value":0.6936603115840247},
{"id":"00001_387193","key":["00001","CADENCIER",0],"value":0.6936603115840247},
{"id":"00001_387848","key":["00001","CADENCIER",1],"value":-0.29332158594360835},
{"id":"00001_387848","key":["00001","CADENCIER",1],"value":-0.29332158594360835},
{"id":"00001_388313","key":["00001","CADENCIER",1],"value":-0.0461553701861542},
{"id":"00001_388313","key":["00001","CADENCIER",1],"value":-0.0461553701861542},
{"id":"00001_388806","key":["00001","CADENCIER",1],"value":-0.04833054041013961},
{"id":"00001_388806","key":["00001","CADENCIER",1],"value":-0.04833054041013961},
{"id":"00001_388897","key":["00001","CADENCIER",1],"value":-0.25761199232338083},
{"id":"00001_388897","key":["00001","CADENCIER",1],"value":-0.25761199232338083},
{"id":"00001_435016","key":["00001","CADENCIER",1],"value":-0.037149057843773745},
{"id":"00001_435016","key":["00001","CADENCIER",1],"value":-0.037149057843773745}
...
]}
I want to reduce to group by key and return the count of values of each key as well as some other calculation on the values.
I did this:
function (key, values, rereduce) {
var result = {};
var ecartsSum;
for(var i = 0; i < values.length; i++) {
ecartsSum =+ values[i];
}
result.productsNumber = values.length;
result.index = 100 + (Math.tan(ecartsSum/values.length)) * 100
return result;
}
When I request the view using the key ["00001","CADENCIER",0]
I get this result :
{
"productsNumber": 3,
"index": null
}
which is not at all the result I was expecting.
PS: I use these options to select :
connection_timeout=600000000&full_set=true&group=true&inclusive_end=true&key=%5B%2200001%22,%22CADENCIER%22,0%5D&limit=6&reduce=true&skip=0&stale=false
Not all values for a given key are passed to the reduce function at one time. The MapReduce view will work on subsets of the data, reducing each subset and combining them using the same reduce function until all values have been processed.
You'll need to use the rereduce argument so the function can reduce the output from previous calls to itself.
From the Re-reduce Argument documentation:
In order to handle incremental map/reduce functionality (i.e. updating an existing view), each function must also be able to handle and consume the functions own output. This is because in an incremental situation, the function must be handle both the new records, and previously computed reductions.
Try something like this example from the documentation:
function(key, values, rereduce) {
var result = {total: 0, count: 0};
for(i=0; i < values.length; i++) {
if(rereduce) {
result.total = result.total + values[i].total;
result.count = result.count + values[i].count;
} else {
result.total = sum(values);
result.count = values.length;
}
}
return(result);
}
I just want to change all the keys in batchesX. But I can't seem to alter all keys, because of concat. This is what I learned from post.
Please advise how I can change all keys with numbers.
var batchesX = '[{"batch":"0010002033"},{"batch":"0010001917"},{"batch":"0000020026"},{"batch":"0000017734"},'+
'{"batch":"0000015376"},{"batch":"0000014442"},{"batch":"0000014434"},{"batch":"0000014426"},'+
'{"batch":"0000013280"},{"batch":"0000012078"},{"batch":"0000012075"},{"batch":"0000012072"},'+
'{"batch":"0000011530"},{"batch":"0000011527"},{"batch":"0000011342"},{"batch":"0000010989"},'+
'{"batch":"0000010477"},{"batch":"0000008097"},{"batch":"0000007474"},{"batch":"0000006989"},'+
'{"batch":"0000004801"},{"batch":"0000003566"},{"batch":"0000003565"},{"batch":"0000001392"},'+
'{"batch":"0000001391"},{"batch":"0000000356"},{"batch":"0000"},{"batch":"000"},{"batch":""},'+
'{"batch":null}]'; // 30 elements
//in JSON text
var batchi = "batch";
var obj_batchesY = JSON.parse(batchesX);
console.debug(obj_batchesY);
var obj_batchesYlength = obj_batchesY.length;
console.debug(obj_batchesYlength);
var obj_batchesX = JSON.parse(batchesX,
function(k,v)
{
for(var i=1; i <= obj_batchesYlength; i++ )
{
if(k=="batch")
{
this.batchi.concat(string(i)) = v;
}
else
return v;
}
}
);
console.debug(obj_batchesX);
Is the code too long winded?
Many thanks in advance.
Clement
The return value of the reviver function only replaces values. If you need to replace keys, then use stringify and replace before the parse call, like this:
JSON.parse(JSON.stringify({"alpha":"zulu"}).replace('"alpha":','"omega":'))
Here is how to replace all numeric keys:
function newkey()
{
return Number(Math.random() * 100).toPrecision(2) + RegExp.$1
}
//Stringify JSON
var foo = JSON.stringify({"123":"ashanga", "12":"bantu"});
//Replace each key with a random number without replacing the ": delimiter
var bar = foo.replace(/\d+("?:)/g, newkey)
//Parse resulting string
var baz = JSON.parse(bar);
Make sure each replaced key is unique, since duplicate keys will be removed by the parse method.
I'm looking to get/display the key name of an object in AS3.
I have for example :
var obj:Object = {key:"value"};
Here I try to display "key" (not its value).
The goal is to be able to merge two objects together.
Any idea ?
Thanks !
To get at the keys of an object you need to loop over them:
for (var key:String in obj) {
trace("key:", key, "value:", obj[key]);
}
Thus, merging obj1 and obj2 (with anything from the second overwriting the first) would look like this:
var merged:Object = {};
var key:String = "";
for (key in obj1) {
merged[key] = obj1[key];
}
for (key in obj2) {
merged[key] = obj2[key];
}