ValueIterator.toArray().indexOf() returns unexpected results? - javascript

UPDATE: this is NOT a question on javascript alone, but related to the javascript implementation on the MarkLogic platform.
As the title of this question points out it is about the specific behaviour of the ValueIterator that is returned by the xdmp.userRoles() function.
I am trying to see if a user has a certain role in MarkLogic Security database, therefor I have done this :
declareUpdate();
var pid = '7610802';
// TODO validate that user can do this
var spo = 'scc-proj-' + pid + '-owner';
var spm = 'scc-proj-' + pid + '-member';
var spam = 'scc-proj-' + pid + '-adv-member';
// we need the security db Ids of these roles
var spoId = xdmp.role(spo);
var spamId = xdmp.role(spam);
var acceptedRoleIds = [spamId,spoId];
// get roleIds from sec db for this user
var userRoleIds = xdmp.userRoles('scc-user-1');
// map ValueIterator to array
var userRoleIdsArray = userRoleIds.toArray();
Now the userRoleIdsArray holds ids as unnsigned long like this:
[
"1088529792688125909",
"1452323661308702627",
"10258509559147330558",
"10161853410412530308",
"6677433310138437512",
"12773061729023600875",
"7482704131174481508",
"3191093315651213021", <<<<< this is the one!!!
"5126952842460325403",
"7089338530631756591",
"15520654661378671735",
"13041542794130379697"
]
Now indexOf() gives me -1 aka not found
userRoleIdsArray.indexOf(3191093315651213021);
OR
userRoleIdsArray.indexOf("3191093315651213021");
Gives :
-1
While
userRoleIdsArray[7]==3191093315651213021;
Gives :
true
What am I missing here? Is this not the way to use indexOf() ?
UPDATE >>> Stuff below was 'on-the-side' but turns out to be distracting from the above core question. The behaviour below is answered by #DaveCassel's comment.
btw on the created array acceptedRoleIds it is even more strange:
acceptedRoleIds.indexOf(spoId);
works
acceptedRoleIds.indexOf(3191093315651213021);
does not?
Could this large number error in javascript be relevant?

You want to find a String, not a number. Use: userRoleIdsArray.indexOf("3191093315651213021");
This works:
var array = [
"1088529792688125909",
"1452323661308702627",
"10258509559147330558",
"10161853410412530308",
"6677433310138437512",
"12773061729023600875",
"7482704131174481508",
"3191093315651213021",
"5126952842460325403",
"7089338530631756591",
"15520654661378671735",
"13041542794130379697"
];
var n = array.indexOf("13041542794130379697");
document.write(n);
output: 11

The mismatch is that ValueIterator.toArray() returns an array of Values (Value[]). When you call .indexOf, you're passing in a string or an unsignedLong rather than a value. Because the types don't match, .indexOf() doesn't report a match.
You can solve the problem by iterating through the loop. Note that I use the '==' operator rather than '==='; the type conversion is needed.
// get roleIds from sec db for this user
var userRoleIds = xdmp.userRoles('my-user');
// map ValueIterator to array
var userRoleIdsArray = userRoleIds.toArray();
var target = 15520654661378671735;
var index = -1;
for (var i in userRoleIdsArray) {
if (userRoleIdsArray[i] == target) {
index = i;
}
}
index

Related

Duplicates in array of numbers failing

I have the following function to check for duplicates;
function hasDuplicates(array) {
return (new Set(array)).size !== array.length;
}
I use this to check if someone has not provided two of the same items to sell from his or her inventory, cause we won't allow him or her getting twice the amount of money he or her should.
It worked for a while but then I think someone who meant harm came in and breached it somehow. I've been testing for a while now and found that I can bypass this sytem by having my array like the following (ids of my inventories);
var array = [312, 329, 932]; //normal > should bypass
var array = [333, 333, 333]; //not normal > can't bypass
but then if you do this it allows you to bypass;
var array = [333, '333/', '333//'];
is there any way I can solve this quickily?
Edit
Database query that looks up the data anyways even if its provided as 333// strings.
database.query('SELECT * FROM `inventories` WHERE `id` = ' + database.pool.escape(id), function(err, row){
array.every(n => Number.isInteger(n))
Just ensure that everything is a number in the array.
You can do something like the following with hash,
and use regex to remove the special characters and leave it with a number only, (casting it to Number is also recommended).
var array = ['333//', 212, '333/'];
var hash = {};
array.forEach(num => {
if(!Number.isInteger(num)) {
num = Number(num.replace(/[^\w\s]/gi, ''));
}
if(!hash[num]) {
hash[num] = num;
}
})
var newArray = Object.values(hash);
console.log(newArray);

How to remove all characters before specific character in array data

I have a comma-separated string being pulled into my application from a web service, which lists a user's roles. What I need to do with this string is turn it into an array, so I can then process it for my end result. I've successfully converted the string to an array with jQuery, which is goal #1. Goal #2, which I don't know how to do, is take the newly created array, and remove all characters before any array item that contains '/', including '/'.
I created a simple work-in-progress JSFiddle: https://jsfiddle.net/2Lfo4966/
The string I receive is the following:
ABCD,ABCD/Admin,ABCD/DataManagement,ABCD/XYZTeam,ABCD/DriverUsers,ABCD/RISC
ABCD/ in the string above can change, and may be XYZ, MNO, etc.
To convert to an array, I've done the following:
var importUserRole = 'ABCD,ABCD/Admin,ABCD/DataManagement,ABCD/XYZTeam,ABCD/DriverUsers,ABCD/RISC';
var currentUserRole = importUserRole.split(',');
Using console.log, I get the following result:
["ABCD", "ABCD/Admin", "ABCD/DataManagement", "ABCD/XYZTeam", "ABCD/DriverUsers", "ABCD/RISC"]
I'm now at the point where I need the code to look at each index of array, and if / exists, remove all characters before / including /.
I've searched for a solution, but the JS solutions I've found are for removing characters after a particular character, and are not quite what I need to get this done.
You can use a single for loop to go through the array, then split() the values by / and retrieve the last value of that resulting array using pop(). Try this:
for (var i = 0; i < currentUserRole.length; i++) {
var data = currentUserRole[i].split('/');
currentUserRole[i] = data.pop();
}
Example fiddle
The benefit of using pop() over an explicit index, eg [1], is that this code won't break if there are no or multiple slashes within the string.
You could go one step further and make this more succinct by using map():
var importUserRole = 'ABCD,ABCD/Admin,ABCD/DataManagement,ABCD/XYZTeam,ABCD/DriverUsers,ABCD/RISC';
var currentUserRole = importUserRole.split(',').map(function(user) {
return user.split('/').pop();
});
console.log(currentUserRole);
You can loop through the array and perform this string replace:
currentUserRole.forEach(function (role) {
role = role.replace(/(.*\/)/g, '');
});
$(document).ready(function(){
var A=['ABCD','ABCD/Admin','ABCD/DataManagement','ABCD/XYZTeam','ABCD/DriverUsers','ABCD/RISC'];
$.each(A,function(i,v){
if(v.indexOf('/')){
var e=v.split('/');
A[i]=e[e.length-1];
}
})
console.log(A);
});
You could replace the unwanted parts.
var array = ["ABCD", "ABCD/Admin", "ABCD/DataManagement", "ABCD/XYZTeam", "ABCD/DriverUsers", "ABCD/RISC"];
array = array.map(function (a) {
return a.replace(/^.*\//, '');
});
console.log(array);
var importUserRole = 'ABCD,ABCD/Admin,ABCD/DataManagement,ABCD/XYZTeam,ABCD/DriverUsers,ABCD/RISC';
var currentUserRole = importUserRole.split(',');
for(i=0;i<currentUserRole.length;i++ ){
result = currentUserRole[i].split('/');
if(result[1]){
console.log(result[1]+'-'+i);
}
else{
console.log(result[0]+'-'+i);
}
}
In console, you will get required result and array index
I would do like this;
var iur = 'ABCD,ABCD/Admin,ABCD/DataManagement,ABCD/XYZTeam,ABCD/DriverUsers,ABCD/RISC',
arr = iur.split(",").map(s => s.split("/").pop());
console.log(arr);
You can use the split method as you all ready know string split method and then use the pop method that will remove the last index of the array and return the value remove pop method
var importUserRole = ABCD,ABCD/Admin,ABCD/DataManagement,ABCD/XYZTeam,ABCD/DriverUsers,ABCD/RISC';
var currentUserRole = importUserRole.split(',');
for(var x = 0; x < currentUserRole.length; x++;){
var data = currentUserRole[x].split('/');
currentUserRole[x] = data.pop();
}
Here is a long way
You can iterate the array as you have done then check if includes the caracter '/' you will take the indexOf and substact the string after the '/'
substring method in javaScript
var importUserRole = 'ABCD,ABCD/Admin,ABCD/DataManagement,ABCD/XYZTeam,ABCD/DriverUsers,ABCD/RISC';
var currentUserRole = importUserRole.split(',');
for(var x = 0; x < currentUserRole.length; x++){
if(currentUserRole[x].includes('/')){
var lastIndex = currentUserRole[x].indexOf('/');
currentUserRole[x] = currentUserRole[x].substr(lastIndex+1);
}
}

count the number of rows in 2d array

Description:
I am adding the data in my 2D array like the following
my_2d_array['user1'] = {'id':name,'socket':socket};
my_2d_array['user2'] = {'id':name,'socket':socket};
This 2D array keeps the record of all the connected users id and their respective sockets ...
I want to display the number of users connected .. So far to do that would be to count the number of rows in the array and display it
I have tried following:
my_2d_array[].length; // this gives nothing
my_2d_array.length; // this outputs 0 (as number)
What should I do to get the number of rows
UPDATE
I declared my array like this
var my_2d_array = [];
This could work for you
// initialize
var my_2d_array = {};
// add users
my_2d_array["user1"] = ...
my_2d_array["user2"] = ...
// get number of users
Object.keys(my_2d_array).length;
//=> 2
You should consider using users instead of my_2d_array though. It communicates better and the actual data type is an Object, not specifically an Array.
Use push method
my_2d_array.push({'id':name,'socket':socket});
my_2d_array.push({'id':name,'socket':socket});
And my_2d_array.length to get the count
It looks like you are trying to figure out how many keys are in your javascript object my_2d_array.
You should be able to use Object.keys()
Here is a JsFiddle.
var my_2d_array = {};
var name = "Hello";
var socket = "World";
my_2d_array['user1'] = {'id':name,'socket':socket};
var name = "Hello2";
var socket = "World2";
my_2d_array['user2'] = {'id':name,'socket':socket};
alert( Object.keys(my_2d_array).length );

access javascript array element by JSON object key

I have an array that looks like this
var Zips = [{Zip: 92880, Count:1}, {Zip:91710, Count:3}, {Zip:92672, Count:0}]
I would like to be able to access the Count property of a particular object via the Zip property so that I can increment the count when I get another zip that matches. I was hoping something like this but it's not quite right (This would be in a loop)
Zips[rows[i].Zipcode].Count
I know that's not right and am hoping that there is a solution without looping through the result set every time?
Thanks
I know that's not right and am hoping that there is a solution without
looping through the result set every time?
No, you're gonna have to loop and find the appropriate value which meets your criteria. Alternatively you could use the filter method:
var filteredZips = Zips.filter(function(element) {
return element.Zip == 92880;
});
if (filteredZips.length > 0) {
// we have found a corresponding element
var count = filteredZips[0].count;
}
If you had designed your object in a different manner:
var zips = {"92880": 1, "91710": 3, "92672": 0 };
then you could have directly accessed the Count:
var count = zips["92880"];
In the current form, you can not access an element by its ZIP-code without a loop.
You could transform your array to an object of this form:
var Zips = { 92880: 1, 91710: 3 }; // etc.
Then you can access it by
Zips[rows[i].Zipcode]
To transform from array to object you could use this
var ZipsObj = {};
for( var i=Zips.length; i--; ) {
ZipsObj[ Zips[i].Zip ] = Zips[i].Count;
}
Couple of mistakes in your code.
Your array is collection of objects
You can access objects with their property name and not property value i.e Zips[0]['Zip'] is correct, or by object notation Zips[0].Zip.
If you want to find the value you have to loop
If you want to keep the format of the array Zips and its elements
var Zips = [{Zip: 92880, Count:1}, {Zip:91710, Count:3}, {Zip:92672, Count:0}];
var MappedZips = {}; // first of all build hash by Zip
for (var i = 0; i < Zips.length; i++) {
MappedZips[Zips[i].Zip] = Zips[i];
}
MappedZips is {"92880": {Zip: 92880, Count:1}, "91710": {Zip:91710, Count:3}, "92672": {Zip:92672, Count:0}}
// then you can get Count by O(1)
alert(MappedZips[92880].Count);
// or can change data by O(1)
MappedZips[92880].Count++;
alert(MappedZips[92880].Count);
jsFiddle example
function getZip(zips, zipNumber) {
var answer = null;
zips.forEach(function(zip){
if (zip.Zip === zipNumber) answer = zip;
});
return answer;
}
This function returns the zip object with the Zip property equal to zipNumber, or null if none exists.
did you try this?
Zips[i].Zip.Count

Remove items from based of substring in array jquery

This might seems a very newbie sort of question, but I am struggling with this as of now and seek some help.
Here is a example array in JavaScript
var SelectedFilters = ["[size:12:12]","[size:12:12]","[size:13:13]","[size:14:14]", "[color:14:14]","[color:14:14]","[type:14:14]","[type:14:14]"];
Now I wish to remove certain items from this array based on a search term, now the search term contains only a part of string such as
var searchTerm1 = 'size'
var searchTerm2 = 'color'
I have already tried the following code, but its not working:
var i = SelectedFilters.indexOf(searchTerm1);
if (i != -1)
{
SelectedFilters.splice(i, 1);
}
I have also tried running to through for loop, to iterate on all items, but again search failed as its not able to match 'size' OR 'color'
What I am looking: if searchterm1 is used, the resulted output will be like:
["[color:14:14]","[color:14:14]","[type:14:14]","[type:14:14]"];
and in case of searchterm2 is used the resulted output should be:
["[size:12:12]","[size:12:12]","[size:13:13]","[size:14:14]","[type:14:14]","[type:14:14]"];
It would be great if anyone can solve this puzzle, I am also trying to find a solution in the meantime.
Your attempt didn't work because .indexOf() on an Array looks for an exact match.
Since according to your question and comment you need to mutate the original Array, you should loop over the array and test each string individually and then call .splice() every time you find one that needs to be removed.
var SelectedFilters = ["[size:12:12]","[size:12:12]","[size:13:13]","[size:14:14]", "[color:14:14]","[color:14:14]","[type:14:14]","[type:14:14]"];
var searchTerm1 = 'size'
var searchTerm2 = 'color'
for (var i = SelectedFilters.length-1; i > -1; i--) {
if (SelectedFilters[i].startsWith(searchTerm1, 1)) {
SelectedFilters.splice(i, 1)
}
}
document.querySelector("pre").textContent =
JSON.stringify(SelectedFilters, null, 2)
<pre></pre>
The loop used above goes in reverse. This is important since every time we do a .splice(), the array gets reindexed, so if we went forward, we would end up skipping over adjacent items to be removed.
The .startsWith() method checks if the string starts with the given search term. I passed the second parameter a value of 1 so that it starts searching on the second character.
You can use filter method of array
var searchTerm = "size";
SelectedFilters = SelectedFilters.filter(function(val){
return val.indexOf( searchTerm ) == -1;
});
You can do it with Array#filter,
var searchTerm1 = 'size';
var result = SelectedFilters.filter(v => !v.includes(searchTerm1));
console.log(result); //["[color:14:14]","[color:14:14]","[type:14:14]","[type:14:14]"];
If you want to alter the original array then do,
var SelectedFilters = ["[size:12:12]", "[size:12:12]", "[size:13:13]", "[size:14:14]", "[color:14:14]", "[color:14:14]", "[type:14:14]", "[type:14:14]"];
var searchTerm1 = 'size',cnt = 0, len = SelectedFilters.length - 1;
while (cnt <= len) {
if (SelectedFilters[len - cnt].includes(searchTerm1)) SelectedFilters.splice(len - cnt, 1);
cnt++;
}
console.log(SelectedFilters);
DEMO

Categories

Resources