How may I retrieve an element that exists in two different arrays of the same document.
For example. In Posts collection, document has the fields 'interestbycreator' and 'interestbyreader.' Each field contain user Ids.
'interestbycreator': //an array of ids here. IdA, idB, IdC, IdD, IdE,
'interestbyreader': //an array of ids here. IdB, idE, iDF
Basically I wish to find all the ids that exist in both arrays, so that should be IdB and IdE.
I am able to pluck all the values from an array with underscore and store them in a variable. Can they be compared to each other this way and return duplicates? Or can someone shed some light on another solution.
Example to retrieve all Ids from 'interestbyreader
var interestbypostcreater = Posts.find({_id: Meteor.user().profile.postcreated[0]}, {fields: {interestbyreader: 1}}).fetch();
var interestedReaderIds = _.chain(interestbypostcreator).pluck('interestbyreader').flatten().value();
Assume I have the other array 'interestbycreator' stored in a variable called interestIdcreator, can they be compared to find duplicates and return these duplicates?
As saimeunt said in the comments when you have access to underscore use intersection but you can also do it with plain javascript:
var x = ['IdA', 'idB', 'IdC', 'IdD', 'IdE'];
var y = ['idB', 'IdE', 'IdF'];
var z = x.filter(function(val) {
return y.indexOf(val) != -1;
});
console.log(z);
The array z contains the double entries then.
Credits to https://stackoverflow.com/a/14930567/441907
As Saimeunt pointed out, it can be done as
var x = ['IdA', 'idB', 'IdC', 'IdD', 'IdE'];
var y = ['idB', 'IdE', 'IdF'];
var z = _.intersection(x, y);
Related
I am trying to loop through a dictionary of customers and save energy usage data, but for some customers when I try to change the values in their usage dictionary it will also change a completely different customer's value. I have a nested dictionary with customer utility information, the top-level key being a unique internal ID.
I stripped my code down to a single loop, looping through the top-level keys and setting the same month's usage for all customers in the dictionary to be the value of the iterator. After that, as shown in the code sample below, I log the values for three customers. After that, I increment only one of those customer's usage, and log the values again. The console shows that two over the customer's have dictionaries that are tied together somehow, but I can't figure out why or how to solve this. I can't discern any pattern in the keys of the linked customers, either.
Structure of the nested dictionary:
CustDict =
{"N0100000XXXXXX" =
{"name" = "XXXX"},
{"address" = "XXXX"},
{"meter_read_dates" =
{"2021-05-13" =
{"usage" = "XXXX"}
}
}
}
Stripped down code I used to demonstrate what is happening as simply as possible (real ID values):
Logger.log(custDict["N01000009700816"]["meter_read_dates"]["2021-05-13"]["usage"])
Logger.log(custDict["N01000000419887"]["meter_read_dates"]["2021-05-13"]["usage"])
Logger.log(custDict["N01000012580668"]["meter_read_dates"]["2021-05-13"]["usage"])
custDict["N01000009700816"]["meter_read_dates"]["2021-05-13"]["usage"] =
custDict["N01000009700816"]["meter_read_dates"]["2021-05-13"]["usage"] + 1
Logger.log(custDict["N01000009700816"]["meter_read_dates"]["2021-05-13"]["usage"])
Logger.log(custDict["N01000000419887"]["meter_read_dates"]["2021-05-13"]["usage"])
Logger.log(custDict["N01000012580668"]["meter_read_dates"]["2021-05-13"]["usage"])
Console Output:
11:54:56 AM Info 346.0
11:54:56 AM Info 346.0
11:54:56 AM Info 322.0
11:54:56 AM Info 347.0
11:54:56 AM Info 347.0
11:54:56 AM Info 322.0
Code used to create the CustDict dictionary:
stmtCR = conn.prepareStatement('SELECT cust_id, utility_account, cycle_id, read_cycle FROM customers')
results = stmtCR.executeQuery()
resultsMetaData = results.getMetaData()
numCols = resultsMetaData.getColumnCount();
results.last();
numRows = results.getRow();
i = 0
results.first()
var custDict = {}
while (i < numRows)
{
custDict[results.getString(1)] = {}
custDict[results.getString(1)]["id"] = results.getString(1)
custDict[results.getString(1)]["utility_account"] = results.getString(2)
custDict[results.getString(1)]["cycle_id"] = results.getString(3)
custDict[results.getString(1)]["read_cycle"] = results.getString(4)
results.next()
i++;
}
for (i = 0; i < Object.keys(custDict).length; i++)
{
tempCust = custDict[Object.keys(custDict)[i]]
tempCycleId = tempCust["cycle_id"]
tempReadCycle = tempCust["read_cycle"]
tempCust["meter_read_dates"] = cycleIdShdDict[tempCycleId][tempReadCycle]
custDict[Object.keys(custDict)[i]] = tempCust
}
cycleIdShdDict is a seperate dictionary that contains a set of dates associated with each cycle_id and read_cycle
I suspect the problem is that Object.keys(custDict) is returning the keys in a different order at different places in the for loop. So you're getting the object from one key, and then assigning it to a different key.
There's no need to assign back to custDict[Object.keys(custDict)[i]] since you're modifying the object in place, not a copy.
But instead of looping through the keys, loop through the values and modify them.
Object.values(custDict).forEach(tempCust => {
let tempCycleId = tempCust["cycle_id"];
let tempReadCycle = tempCust["read_cycle"];
tempCust["meter_read_dates"] = cycleIdShdDict[tempCycleId][tempReadCycle];
});
I am looping through a collection of blog posts to firstly push the username and ID of the blog author to a new array of arrays, and then secondly, count the number of blogs from each author. The code below achieves this; however, in the new array, the username and author ID are no longer separate items in the array, but seem to be concatenated into a single string. I need to retain them as separate items as I need to use both separately; how can I amend the result to achieve this?
var countAuthors = [];
blogAuthors = await Blog.find().populate('authors');
blogAuthors.forEach(function(blogAuthor){
countAuthors.push([blogAuthor.author.username, blogAuthor.author.id]);
})
console.log(countAuthors);
// Outputs as separate array items, as expected:
// [ 'author1', 5d7eed028c298b424b3fb5f1 ],
// [ 'author2', 5dd8aa254d74b30017dbfdd3 ],
var result = {};
countAuthors.forEach(function(x) {
result[x] = (result[x] || 0) + 1;
});
console.log(result);
// Username and author ID become a single string and cannot be accessed as separate array items
// 'author1,5d7eed028c298b424b3fb5f1': 15,
// 'author2,5dd8aa254d74b30017dbfdd3': 2,
Update:
Maybe I can explain a bit further WHY on what to do this. What I am aiming for is a table which displays the blog author's name alongside the number of blogs they have written. However, I also want the author name to link to their profile page, which requires the blogAuthor.author.id to do so. Hence, I need to still be able to access the author username and ID separately after executing the count. Thanks
You could use String.split().
For example:
let result = 'author1,5d7eed028c298b424b3fb5f1'.split(',')
would set result to:
['author1' , '5d7eed028c298b424b3fb5f1']
You can then access them individually like:
result[1] //'5d7eed028c298b424b3fb5f1'
Your issue is that you weren't splitting the x up in the foreach callback, and so the whole array was being converted to a string and being used as the key when inserting into the results object.
You can use array destructuring to split the author name and blog id, and use them to optionally adding a new entry to the result object, and then update that result.
countAuthors = [
['author1', 'bookId1'],
['author2', 'bookId2'],
['author1', 'bookId3'],
['author1', 'bookId4'],
['author2', 'bookId5']
]
var result = {};
countAuthors.forEach(([author, id]) => {
if (result[author] === undefined) {
result[author] = {count: 0, blogIds: []};
}
result[author].count += 1;
result[author].blogIds.push(id);
});
console.log(result);
I've got objects 'ing' with a field named 'id' and another one called 'fObj' with a field named 'contain'.
By using ng-repeat i'd like to show only these 'ing' objects where ing.id is a part of fObj.contain
e.g.
ing=[{id: 1,field: value},{id:2, field: othervalue},{id:3, field: cat}];
fObj={field1: value1, field: value2, contain: ':1:3:'};
By having this contain value I'd like to show only ing's with id=1 and id=3
Yeah, I know there are two types of data (number and string) but even if i changed numbers to strings it still didn't work
I just dont't know how to make it works. It's probably some kind of custom filter, but I've tried couples and nothing happend.
I would be glad if you suggest me a solution.
Thanks
In your controller,
var ids = fObj.contain.split(':');
// the array for your ng-repeat
var displayIng = [];
// loop the objects, see if the id exists in the list of id's
// retrieved from the split
for(i = 0; i < ing.length; i++) {
if(ids.indexOf(ing.id.toString()) displayIng.push(ing[i]);
}
I would split the numbers out of fObj.contain; and use them as hashmap object keys for simple filtering of the array
var ing=[{id: 1},{id:2},{id:3}];
var fObj={contain: ':1:3:'};
var IDs = fObj.contain.split(':').reduce(function(a,c){
a[c]=true;
return a;
},{});
// produces {1:true,3:true}
var filtered = ing.filter(function(item){
return IDs[item.id];
});
console.log(filtered)
I have 2 Arrays and one is 2 dimensional and another is 1 dimensional. I need to compare both and need to store there common data in another array. I tried the below approach:-
tw.local.listtodisplayNW = new tw.object.listOf.listtodisplayNWBO();
//if(tw.local.SQLResults[0].rows.listLength >
// tw.local.virtualServers.listLength)
var k=0;
for (var i=0;i<tw.local.SQLResults[0].rows.listLength;i++)
{
log.info("Inside SQLResults loop - For RuntimeID: "
+tw.local.SQLResults[0].rows[i].data[3]);
for(var j=0;j<tw.local.virtualServers.listLength;j++)
{
log.info("Inside API loop - For RuntimeID: "
+tw.local.virtualServers[j].runtimeid);
if(tw.local.SQLResults[0].rows[i].data[3] ==
tw.local.virtualServers[j].runtimeid)
{
tw.local.listtodisplayNW[k] = new tw.object.listtodisplayNWBO();
tw.local.listtodisplayNW[k].vsysName =
tw.local.virtualServers[j].virtualSystemName;
tw.local.listtodisplayNW[k].vsysID =
tw.local.virtualServers[j].virtualSystemId;
tw.local.listtodisplayNW[k].serverName =
tw.local.virtualServers[j].serverName;
tw.local.listtodisplayNW[k].serverID =
tw.local.virtualServers[j].serverId;
tw.local.listtodisplayNW[k].runtimeID =
tw.local.virtualServers[j].runtimeid;
//tw.local.listtodisplayNW[k].IPAddress =
tw.local.virtualServers[j].nics[j].ipAddress;
log.info("VsysName:
"+tw.local.listtodisplayNW[k].vsysName+"RuntimeID:
"+tw.local.listtodisplayNW[k].runtimeID);
//tw.local.listtodisplayNW[k] = new
tw.object.listtodisplayNWBO();
tw.local.listtodisplayNW[k].currentSpeed =
tw.local.SQLResults[0].rows[i].data[5];
log.info("VsysName:
"+tw.local.listtodisplayNW[k].vsysName+"RuntimeID:
"+tw.local.listtodisplayNW[k].runtimeID+"CurrentSpeed:
"+tw.local.listtodisplayNW[k].currentSpeed);
if(tw.local.listtodisplayNW[k].currentSpeed != "100 Mbps")
{
tw.local.listtodisplayNW[k].desiredSpeed = "100 Mbps";
}
else
{
tw.local.listtodisplayNW[k].desiredSpeed = "1 Gbps";
}
log.info("DesiredSpeed:
"+tw.local.listtodisplayNW[k].desiredSpeed);
k++;
}
}
log.info("Length of
listtodisplayNW"+tw.local.listtodisplayNW.listLength);
}
In above code SQLResults is a 2-d array and virtualServers is a 1-D array.
I need to compare both these array and common data need to be store in another array. Here performance is not good. Is there any other way to do this efficiently. Please make a needful favour and Thanks in advance.
Assuming integer data, the following example works on the theme of array implementation of set intersection, which will take care of performance.
Convert 2D array to 1D.
var 2DtoIDArray = 2DArray.join().split(",");
Create an array named marker whose purpose is to serve as a lookup that element.
This needs to be done as follows.
Iterate through the smaller array, say 1DArray and keep setting marker as follows throughout iteration.
marker[1DArray[counter]]='S1';
Now iterate through 2Dto1DArray array(you may use nested loop iteration if you dont want to convert it to 1 dimesnional) and for each element
of this array check if its marked as 'S1' in the marker lookup array.
If yes, keep adding the elements in the commonElementsArray.
Follow this simple approach
Since the matching condition is only one between the two large arrays, create two maps (one for each array) to map each record against that attribute which is to be matched
For SQLResults
var map1 = {};
tw.local.SQLResults[0].rows.each( function(row){
map1[ row.data[3] ] = row;
});
and similarly for virtual servers
var map2 = {};
tw.local.virtualServers.each( function(vs){
map2[ vs.runtimeid ] = vs;
});
Now iterate these two maps wrt to their keys and set the values in new array
new array being tw.local.listtodisplayNW
tw.local.listtodisplayNW = [];
Object.keys( map1 ).forEach( function( key ){
if( map2[ key ] )
{
//set the values in tw.local.listtodisplayNW
}
})
Complexity of the approach is simply O(n) since there is no nested loops.
We're building a site with ExpressionEngine. We are running a SQL query to gather up all member IDs for a specific member group. After that, we are using EE tags to get data from a custom member field for each member ID.
The ID and field data need to stay paired, as we will be populating a drop-down so that the ID is the value and the field data is the text, so we are currently putting them into a JS array as key/value pairs. The call is as follows:
var array= [
{exp:query sql="SELECT * FROM exp_members WHERE group_id = 5"}
{exp:member:custom_profile_data
member_id="{member_id}"}
{if company != ''}
{{member_id}:"{company}"},
{/if}
{/exp:member:custom_profile_data}
{/exp:query}
};
This gives us the output:
var array = [
{1:"name01"},
{2:"name02"},
{3:"name01"},
{4:"name03"}
];
Now, our problem. We need to remove objects based on duplicate field data (values) only, so the above array would look like this:
var array = [
{1:"name01"},
{2:"name02"},
{4:"name03"}
];
None of these IDs (keys) will ever be the same, but the field data (values) can be. So we want to keep the first KV pair that comes through with a unique value, but remove any subsequent dupes of that value - despite the fact that they will not be true "duplicate values" due to a different ID (key).
Keeping in mind that the KV pairs are all dynamic, is there any possible way to do this via JS so we can create a new array for the cleaned data to pass to the drop-down?
You could handle the duplications by modifying your MySQL query. (In my example, my custom field ID was 1.)
var myArray = [];
{exp:query sql="SELECT MIN(m.member_id) AS co_member_id, d.m_field_id_1 AS company FROM exp_members m INNER JOIN exp_member_data d ON m.member_id = d.member_id WHERE d.m_field_id_1 != '' AND m.group_id > 0 GROUP BY d.m_field_id_1;"}
myArray.push({{co_member_id}: "{company}"});
{/exp:query}
This query would use the first (in the ordinal sense) member_id found; you could also change the MIN to MAX and get the last.
This will give you a clean output in your source, without the need for any additional JS processing. I'd also recommend changing the names of the variables you're outputting as to not conflict in EE's parsing.
I would do it like...
function removeDups(arry){
var tmp = {}, retainIdx=[], newArry=[];
arry.forEach(function(obj, idx){
var val = obj[Object.keys(obj)[0]];
if(val && !tmp[val]){
retainIdx.push(idx);
tmp[val] = true;
}
});
retainIdx.forEach(function(i){
newArry.push(arry[i]);
});
return newArry;
};