I am looking to use angular.equals on two arrays which contain objects, but in order to be more efficient, I want to just compare based on a single id token within each object. What would be a good way to do this in Angular?
To give an example,
Array A1 = [obj1, obj2, obj3] and
Array A2 = [obj4, obj5, obj6].
I want say that A1 = A2, if obj1.id = obj4.id, and obj2.id = obj5.id and obj3.id = obj6.id
you can use the map pattern to extract out the ids to compare, and then determine if the id lists are the same.
function compareById(a, b) {
var aIds = a.map(function(x) { return x.id; } ).unique().sort();
var bIds = b.map(function(x) { return x.id; } ).unique().sort();
return aIds.length == bIds.length &&
aIds.every(function(e,i) { return bIds.indexOf(e) == i; });
}
Related
I have a question: I have two array values, one is custom class object which has a variable classification code and other, which is an array of values of that classificiation type. I need to check if values exists in classification code array and combine results to make one result. The values can be multiples or single and based on that I need to put them in contains filter.
For example at the moment for single value the code is.
private List<Result> FilterbyClassification(List<Result> results)
{
var classificationCodes = "C, R".Split(",").ToList();
return results.Where(f => classificationCodes.All(f.ClassificationCode.Contains)).ToList();
}
It is not giving me any values at the moment. Can you please help
Couple of things you'll need to check:
Remove the blank space in the classificationCodes declaration
var classificationCodes = "C,R".Split(",").ToList();
Change your Linq to use Any instead of All and use the comparison for the classification code. That should give you the result:
// items:
var items = new List<Result>
{
new Result { ClassificationCode = "C", Id = 1},
new Result { ClassificationCode = "C", Id = 2},
new Result { ClassificationCode = "R", Id = 3},
new Result { ClassificationCode = "A", Id = 4}
};
var classificationCodes = "C,R".Split(",").ToList();
var data = items.Where(x => classificationCodes.Any(y => y == x.ClassificationCode)).ToList();
Console.WriteLine(JsonSerializer.Serialize(data));
// Output
[{"Id":1,"ClassificationCode":"C"},{"Id":2,"ClassificationCode":"C"},{"Id":3,"ClassificationCode":"R"}]
Alternatively, you can also you a linq join:
var data = from i in items
join cc in classificationCodes on i.ClassificationCode equals cc
select new Result {ClassificationCode = i.ClassificationCode, Id = i.Id};
Or
var data = items.Join(classificationCodes, i => i.ClassificationCode, cc => cc,
(i, cc) => new Result {ClassificationCode = i.ClassificationCode, Id = i.Id});
I am trying to filter the array 'employee_name' consisting of NaNs and one string element, to exclude any element BUT the string. The context is that I have a spreadsheet containing employee's birth dates, and I'm sending an email notification in case there's a birthday two days from today. My variables look like this:
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Employees');
var range = ss.getRange(2, 1, ss.getLastRow()-1, 1); // column containing the birth dates
var birthdates = range.getValues(); // get the `values` of birth date column
var today = new Date ();
var today = new Date(today.getTime());
var secondDate = new Date(today.getTime() + 48 * 60 * 60 * 1000);
var employee_name = new Array(birthdates.length-1);
And the loop:
for (var i=0;i<=birthdates.length-1;i=i+1){
var fDate = new Date(birthdates[i][0]);
if (fDate.getDate() == secondDate.getDate() &&
fDate.getMonth() == secondDate.getMonth()){
//define variables for outgoing email
for (var j=0; j<=birthdates.length-1;j=j+1){
employee_name[j] = [NaN];
}
employee_name[i] = ss.getRange(i+2,6);
employee_name[i] = employee_name[i].getValues();
}
}
after which the array in question looks like this
Logger.log(employee_name);
[[[Mia-Angelica]], [NaN], [NaN], [NaN], ..., [NaN]]
I have already tried the filter(Boolean), but this isn't working:
employee_name_filtered = employee_name.filter(Boolean);
Logger.log(employee_name_filtered);
returns [[[Mia-Angelica]], [NaN], [NaN], [NaN], ..., [NaN]].
I have also tried filling the non-string array entries with numeric values (instead of NaN) and then apply
employee_name_filtered = employee_name.filter(isFinite);
Logger.log(employee_name_filtered);
returns [[1.0], [2.0], [3.0], ..., [72.0]], so this filter method is working, but then I would need the 'inverse' of that because I want to keep the string.
I need the array within array to store the values at the position of the counter variable where the condition's met (similar to How to store data in Array using For loop in Google apps script - pass array by value).
This is my first time posting a question on SO, so if I overlooked any 'rules' about posting, just let me know and I will provide additional info.
Any help will be appreciated!
EDIT:
what I would like to receive in the end is simply
[[Mia-Angelica]].
The array you are using a 2 dimensional array - meaning it's an array of arrays so the filter method you are using cannot be applied in the same manner.
For this, I suggest you try the below snippet.
function cleanArray() {
var initialArray = [
['Mia-Angelica'],
['Space'],
['2'],
[NaN],
[NaN],
[NaN],
[NaN]
];
var finalArray = [];
for (let i = 0; i < initialArray.length; i++) {
var midArray = initialArray[i].filter(item => (Number.isFinite(item) && item.id !== 0) || !Object.is(item, NaN));
finalArray.push(midArray);
}
console.log(finalArray.filter(item => item != ''));
}
Note
Please bear in mind that getValues will return an Object[][] which is a two-dimensional array of values.
Reference
Apps Script Range Class;
Array.prototype.filter().
I have a for loop that builds an array of data of size n rows by 10 columns.
var dupRows = crossoverDuplicates.getDataRange();
var dupNumRows = dupRows.getNumRows();
var dupValues = dupRows.getValues();
var retreiveNotFound = [];
for (var c = 1; c < dupNumRows; c++) {
var dupRow = dupValues[c];
if (dupRow[10] == "") {
retreiveNotFound.push(dupValues[c]);
}
}
I then use setValues() to paste the array to the bottom of another sheet.
if (retreiveNotFound && retreiveNotFound.length) {
crossover.getRange(lastRowCrossoverData + 1, 2,
retreiveNotFound.length,10 ).setValues(retreiveNotFound);
}
However, the columns on the destination sheet have now moved and I need to paste the first 7 columns of the array in B - H and the last 3 in AV - AX.
Alternatively, I could create two arrays, one with the first 7 columns and another with the last 3 and then paste the separately.
Unfortunately, I can't figure out how to do either.
I'm guessing this has a relatively simple solution, but I'm just not searching for the right key words. Thanks in advance for your help!
Move Google spreadsheet columns into two separate places. Split Google spreadsheet columns up and put them into different columns or two different columns. This code gets data from one place, and then creates two separate arrays, and writes the data into two non-contiguous ranges.
function myFunction() {
var dupRows = crossoverDuplicates.getDataRange();
var dupNumRows = dupRows.getNumRows();
var dupValues = dupRows.getValues();
var B_H_Array = [];
var AV_AX_Array = [];
var AV_AX_TempArray, B_X_TempArray, c, dupRow, thisRowsData;//Define variables
//without assigning a value
for (c = 1; c < dupNumRows; c++) {
dupRow = dupValues[c];
if (dupRow[10] == "") {
thisRowsData = dupValues[c];
B_X_TempArray = thisRowsData.slice(0,7);//Slice out the first elements
AV_AX_TempArray = thisRowsData.slice(7,10);//Slice out last elements
B_H_Array.push(B_X_TempArray);
AV_AX_Array.push(AV_AX_TempArray);
}
}
if (B_H_Array.length) {
crossover.getRange(lastRowCrossoverData + 1, 2,B_H_Array.length, B_H_Array[0].length )
.setValues(B_H_Array);
}
if (AV_AX_Array.length) {
crossover
.getRange(lastRowCrossoverData + 1, 48,AV_AX_Array.length, AV_AX_Array[0].length )
.setValues(AV_AX_Array);
}
}
I have an array of objects with different properties and I need to filter the array in a way that a specific property is not duplicated.
For example:
var array:Array = [{foo:"a1", bar:"b1", baz:"c1"},
{foo:"a2", bar:"b2", baz:"c2"},
{foo:"a3", bar:"b1", baz:"c3"},
{foo:"a1", bar:"b4", baz:"c2"},
{foo:"a0", bar:"b3", baz:"c1"}];
Now suppose I want to filter the objects on the property baz. What is the most efficient way of filtering the array, so that no two elements have the same value for baz after the operation?
In my example, the result should only contain:
var result:Array = [{foo:"a1", bar:"b1", baz:"c1"},
{foo:"a2", bar:"b2", baz:"c2"},
{foo:"a3", bar:"b1", baz:"c3"}]
since the other objects would have duplicate entries for the baz property.
The order of the result array is not important, neither is which object of those with identical values for baz makes it into the result array.
Update:
The object array is used as a dataprovider to populate a s:Datagrid with information about chatrooms. The objects in the array carry related information (like the room's ID on the server and some other config settings).
The baz property I used in my example is actually the ID of the language the chat room is configured to use and I want to create a s:DropDownList with which I can filter the Datagrid for individual languages (e.g. show all rooms that use "German").
It is very likely to have many objects with the same language ID, but I only want each language Id to show up once in the DropDownList.
I need to extract that information from the Datagrids's dataprovider (the source array) and cannot retrieve my languages directly since the DropDownList is part of a generic DatagridHeaderRenderer that is used in many different Datagrids with different data.
private var array:Array = [{foo:"a1", bar:"b1", baz:"c1"},
{foo:"a2", bar:"b2", baz:"c2"},
{foo:"a3", bar:"b1", baz:"c3"},
{foo:"a1", bar:"b4", baz:"c2"},
{foo:"a0", bar:"b3", baz:"c1"}];
private var filteredArray:Array;
private var keys:Object = {};
private function filterArray():void{
filteredArray = arr.filter(removeDupes);
}
private function removeDupes(item:Object, idx:uint, arr:Array):Boolean {
if (keys.hasOwnProperty(item.baz)){
return false;
} else {
keys[item.baz] = item;
return true;
}
}
private function resetFilter():void{
filteredArray = new Array();
keys = {};
}
Modified from multiple sources but primarily: http://blog.flexexamples.com/2007/08/05/removing-duplicate-items-from-an-array-using-the-arrayfilter-method/
Or you could just use an arrayCollection and its built-in filterFunction. See: http://cookbooks.adobe.com/post_Using_the_to_ArrayCollection_s_filterFunction-5441.html
On the surface of it looks like it should work. Using Array.filter is usually about twice the time of doing the same thing in a loop.
I'd argue that' Dom's removeDupes function doesn't do exactly what's required, although it might be a more generic approach (if, for example, the === isn't a good comparison function, then this gives you a way of extending it.) But using hasOwnPropery is a big no-no. You should never touch it - that function only exists for ES compatibility. It is evil otherwise - both a potential security hole (as it is defined on Object.prototype and thus is easy to override for the foreign code) and is slow (for the same reason - the lookup of the functions defined on prototype is slower then those defined in a class).
public function Test()
{
super();
var array:Array = [{foo:"a1", bar:"b1", baz:"c1"},
{foo:"a2", bar:"b2", baz:"c2"},
{foo:"a3", bar:"b1", baz:"c3"},
{foo:"a1", bar:"b4", baz:"c2"},
{foo:"a0", bar:"b3", baz:"c1"}];
this.removeDuplicates(array, "baz").map(this.objectTracer);
// { foo : a3, baz : c3, bar : b1 }
// { foo : a1, baz : c2, bar : b4 }
// { foo : a0, baz : c1, bar : b3 }
}
private function objectTracer(object:Object, index:int, all:Array):void
{
var result:String = "";
for (var p:String in object)
result += ", " + p + " : " + object[p];
if (result) result = result.substr(2);
trace("{ " + result + " }");
}
private function removeDuplicates(array:Array, on:String):Array
{
var result:Array = array.concat();
// note that since we use `Dictionary' the
// the comparison between objects is the same as `==='
// if all values are strings, you can use `Object' to
// save some space.
var hash:Dictionary = new Dictionary();
var temp:Object;
for (var i:int, j:int = result.length - 1; j >= i; j--)
{
temp = result[j][on];
if (temp in hash)
{
result[j] = result[i];
j++; i++;
}
else hash[temp] = true;
}
// note that we could `shift()` until we get to `i'
// but when we do it, we actually reallocate the array every time
// so `slice()' must, in theory, be more efficient
return result.slice(i);
}
I have an ActionScript 3 array that lists pairs of items like this:
pairs[0] = Array('ItemA', 'ItemB');
pairs[1] = Array('ItemA', 'ItemC');
pairs[2] = Array('ItemC', 'ItemD');
pairs[3] = Array('ItemC', 'ItemE');
pairs[4] = Array('ItemF', 'ItemG');
pairs[5] = Array('ItemF', 'ItemH');
And I need to loop over the array in some way to find all overlapping pairs (any pairs that share common pairs).
For example, ItemA is paired with ItemB and ItemC, so they belong in a group together. ItemC is also paired with ItemD and ItemE so they also need to be a part of the first group.
ItemF, ItemG and ItemH do not overlap with any of the items fromt he first group, so they need to be put into their own group.
The resulting array would need to be something like:
groups[0] = Array('ItemA', 'ItemB', 'ItemC', 'ItemD', 'ItemE');
groups[1] = Array('ItemF', 'ItemG', 'ItemH');
Thanks for any help and suggestions!
Edit:
A little bit of a back story; I'm trying to group together movie clips that overlap each other in 2D to create groups or clusters (might be a better word).
So if I have 3 movieclips on the stage and ClipA overlaps with ClipB and ClipB overlaps ClipC (but ClipA doesn't directly overlap ClipC) they should all be grouped together as they are all a part of the same cluster. This way should a new clip overlap any single item in a cluster, it will be added to that group's array.
I've already got the code worked out to find overlapping elements which is producing this pairs list, now I need to condense it into tidy groups.
An algorithm like the example below should work.
NOTE: This is not the most efficient or concise way to write this code (it's certainly more repetitive than it needs to be), but I wanted to keep it clear and simple for this example. [Also, I haven't tested this code--it's presented as pseudo-code only--so if you find an error, please just let me know, and I'll fix it]
var idx:Object = new Object;
var groups:Array = new Array();
for( var i:int = 0; i<pairs.length; ++i ) {
var onePair:Array = pairs[i];
// which groups do the two items belong to?
var g1:Array = idx[onePair[0]];
var g2:Array = idx[onePair[1]];
if( !g1 ) {
// if item #1 is not yet in a group, then add it to item #2's
// existing group, or if neither group exists yet, just create a new one
g1 = g2;
if( !g1 ) {
g1 = [];
groups.push(g1);
}
g1.push( onePair[0] );
// ensure that the idx properly reflects the location of the new item
idx[onePair[0]] = g1;
}
// now do the same for the second item... but g1 will never be null, so
// this case is a little simpler.
if( !g2 ) {
g2 = g1;
g2.push( onePair[1] );
idx[onePair[1]] = g2;
}
if( g1 != g2 ) {
// now, if they're not already the same group, then merge the two
// groups, and update the idx to reflect the merge.
for( var z:int=0; z<g2.length; ++z ) {
idx[g2[z]] = g1;
g1.push( g2[z] );
g2.splice(0);
}
}
}
groups will end up being an array of arrays, just like you asked for -- but there will be a few empty arrays that can be discarded. Just prune (or ignore) the empty ones, and you'll have your groups.
the basic idea here, is that idx provides a lookup table that indicates, throughout the indexing process, for any given item, which group it's in (if any). This allows us to determine whether an item has been encountered previously or not, and if so, to utilize it's existing group.
You can use an Object to keep the track of the association of a pair iten and a group, the key will be each item of your pair.
Here a litle snippet that make the works :
var pairs:Array=[];
pairs[0] = ['ItemA', 'ItemB'];
pairs[1] = ['ItemA', 'ItemC'];
pairs[2] = ['ItemC', 'ItemD'];
pairs[3] = ['ItemC', 'ItemE'];
pairs[4] = ['ItemF', 'ItemG'];
pairs[5] = ['ItemF', 'ItemH'];
// will contain group created
var groups:Array=[];
// will contain association between a pair item and a group
var pair2group:Object={};
// function that turn pairs into groups
function makeGroups(pairs:Array):void{
var pairLen:int = pairs.length;
for (var i:int=0;i<pairLen;i++){
var pair:Array = pairs[i];
var item1:String = pair[0];
var item2:String = pair[1];
var group:Array = pair2group[item1];
// is first pair item already in a group
if (group == null) {
// no so create a new group
group=[];
// create the association
pair2group[item1] = group;
// add the item to the group we have created
group.push(item1);
// add it to all the groups
groups.push(group);
}
// is the second pair item into a grouo
if (pair2group[item2] == null) {
// no so add it to the group where the first item belong
group.push(item2);
// create the association for the second item
pair2group[item2] = group;
}
}
}
// ---- test
makeGroups(pairs);
trace(groups.length);
trace(groups[0]);
trace(groups[1]);
After lots of playing around here's the solution I came up with.
This will take an 2D overlapArray that has pairs and produce a group list with unique values.
I used a in_array() function to duplicate PHP's handy function for finding if an item is already in an array.
for each(var pair:Array in overlapArray) {
var pairInGroup = false;
for each(var group:Array in overlapArrayGroups) {
if(in_array(pair[0],group) || in_array(pair[1],group)) {
if(!in_array(pair[0],group)) {
group.push(pair[0]);
}
if(!in_array(pair[1],group)) {
group.push(pair[1]);
}
pairInGroup = true;
}
}
if(!pairInGroup) {
overlapArrayGroups.push(pair);
}
}
The in_array() function:
public static function in_array( needle:String, haystack:Array ):Boolean {
for( var a = 0; a < haystack.length; a++ ) {
if( haystack[a] == needle ) {
return true;
} else if( haystack[a] is Array ) {
return in_array(needle, haystack[a]);
}
}
return false;
}