Remove duplicate values from arraycollection in flex4 - apache-flex

This is my arraycollection
o = JSON.parse(event.result.toString());
jsonarray = new ArrayCollection(o as Array);
in this array i have a duplicate values of product name, so i wants to remove duplicacy.\
my code is here,its not working please let me know, i am a flex beginner. thanx in advance.
function removeDuplicates(item:Object):Boolean
{
var returnValue:Boolean = false;
if (!myObject.hasOwnProperty(item.ProductName))
{
myObject[item.ProductName] = item;
returnValue = true;
}
prodArray.push(myObject);
return returnValue;
}

Call the filterCollection method given below and in that use the filterfunction to remove duplicates
private var tempObj:Object = {};
private function filterCollection():void {
// assign the filter function
jsonarray.filterFunction = removeDuplicates;
//refresh the collection
jsonarray.refresh();
}
private function removeDuplicates(item:Object):Boolean {
return (tempObj.hasOwnProperty(item.ProductName) ? false : tempObj[item.ProductName] = item && true);
}

Related

How to map lists with ValueInjector

I am using ASP.NET MVC 3.
Can someone please help me clarify what's happening here:
var person = new PersonRepository().Get();
var personViewModel = new PersonViewModel();
personViewModel.InjectFrom<LoopValueInjection>(person)
.InjectFrom<CountryToLookup>(person);
I have a grid on my Index view. Each row is an instance of a CategoryViewModel. So what I do is to get a list of all the categories and then map each Category to a CategoryViewModel, and then pass this list of CategoryViewModels to the view. Hou would I do a mapping like that?
IEnumerable<Category> categoryList = categoryService.GetAll();
I thought the following would work but it doesn't:
// Mapping
IList<CategoryViewModel> viewModelList = new List<CategoryViewModel>();
viewModelList.InjectFrom(categoryList);
AFAIK value injecter doesn't support automatic collection mapping like AutoMapper but you could use a simple LINQ expression and operate on each element:
IEnumerable<Category> categoryList = categoryService.GetAll();
IList<CategoryViewModel> viewModelList = categoryList
.Select(x => new CategoryViewModel().InjectFrom(x)).Cast<CategoryViewModel>()
.ToList();
//source list
IEnumerable<string> items = new string[] { "1", "2" };
// target list
List<int> converted = new List<int>();
// inject all
converted.InjectFrom(items);
And the extension method:
public static ICollection<TTo> InjectFrom<TFrom, TTo>(this ICollection<TTo> to, IEnumerable<TFrom> from) where TTo : new()
{
foreach (var source in from)
{
var target = new TTo();
target.InjectFrom(source);
to.Add(target);
}
return to;
}
ICollection<T> is the interface that got least features but a Add method.
Update
An example using more proper models:
var persons = new PersonRepository().GetAll();
var personViewModels = new List<PersonViewModel>();
personViewModels.InjectFrom(persons);
Update - Inject from different sources
public static ICollection<TTo> InjectFrom<TFrom, TTo>(this ICollection<TTo> to, params IEnumerable<TFrom>[] sources) where TTo : new()
{
foreach (var from in sources)
{
foreach (var source in from)
{
var target = new TTo();
target.InjectFrom(source);
to.Add(target);
}
}
return to;
}
Usage:
var activeUsers = new PersonRepository().GetActive();
var lockedUsers = new PersonRepository().GetLocked();
var personViewModels = new List<PersonViewModel>();
personViewModels.InjectFrom(activeUsers, lockedUsers);
Use this function definition
public static object InjectCompleteFrom(this object target, object source)
{
if (target.GetType().IsGenericType &&
target.GetType().GetGenericTypeDefinition() != null &&
target.GetType().GetGenericTypeDefinition().GetInterfaces() != null &&
target.GetType().GetGenericTypeDefinition().GetInterfaces()
.Contains(typeof(IEnumerable)) &&
source.GetType().IsGenericType &&
source.GetType().GetGenericTypeDefinition() != null &&
source.GetType().GetGenericTypeDefinition().GetInterfaces() != null &&
source.GetType().GetGenericTypeDefinition().GetInterfaces()
.Contains(typeof(IEnumerable)))
{
var t = target.GetType().GetGenericArguments()[0];
var tlist = typeof(List<>).MakeGenericType(t);
var addMethod = tlist.GetMethod("Add");
foreach (var sourceItem in source as IEnumerable)
{
var e = Activator.CreateInstance(t).InjectFrom<CloneInjection>(sourceItem);
addMethod.Invoke(target, new[] { e });
}
return target;
}
else
{
return target.InjectFrom(source);
}
}
For those like me who prefer shortest notations possible
public static ICollection<TTarget> InjectFromList<TTarget, TOrig>(this ICollection<TTarget> target, ICollection<TOrig> source) where TTarget : new()
{
source.Select(r => new TTarget().InjectFrom(r))
.Cast<TTarget>().ToList().ForEach(e => target.Add(e));
return target;
}
public static ICollection<TTarget> InjectFromList<TTarget, TOrig>(this ICollection<TTarget> target, params ICollection<TOrig>[] sources) where TTarget : new()
{
sources.ToList().ForEach(s => s.ToList().Select(r => new TTarget().InjectFrom(r))
.Cast<TTarget>().ToList().ForEach(e => target.Add(e)));
return target;
}
Create a generic list mapper:
public class ValueMapper
{
public static TResult Map<TResult>(object item) where TResult : class
{
return item == null ? null : Mapper.Map<TResult>(item);
}
public static IEnumerable<TResult> MapList<TResult>(IEnumerable<object> items) where TResult : class
{
return items?.Select(i => Mapper.Map<TResult>(i));
}
}
Now you can reference the ValueMapper class wherever you want, and call both Map and MapList
var mydtos = ValueMapper.MapList<MyDto>(dtos);
var mydto = ValueMapper.Map<MyDto>(dto);

Flex sort array collection by inner class

I want to sort an array collection in a way that I am not sure is possible.
Usually when you want to sort you have something like this.
var dataSortField1:SortField = new SortField();
dataSortField1.name = fieldOneToSortBy;
dataSortField1.numeric = fieldOneIsNumeric;
var dataSort:Sort = new Sort();
dataSort.fields = [dataSortField1];
arrayCollection.sort = dataSort;
arrayCollection.refresh();
so if I had a class
public class ToSort {
public var int:a
}
I could type
var ts1:ToSort = new ToSort();
ts1.a = 10;
var ts2:ToSort = new ToSort();
ts2.a = 20;
var arrayCollection:ArrayCollection = new ArrayCollection([ts1, ts2])
var dataSortField1:SortField = new SortField();
dataSortField1.name = "a";
dataSortField1.numeric = true;
var dataSort:Sort = new Sort();
dataSort.fields = [dataSortField1];
arrayCollection.sort = dataSort;
arrayCollection.refresh();
This works fine. My problem is I now have inherited a class that has another class inside it and I need to sort against this as well.
For example
public class ToSort2 {
public var int:a
public var ToSortInner: inner1
}
public class ToSortInner {
public var int:aa
public var int:bb
}
if a is the same in multiple classes then I want to sort on ToSortInner2.aa Is this possible. I have tried to pass in inner1.aa as the sort field name but this does not work.
Hope this is clear. If not I'll post some more code.
Thanks
You need to write a custom sort compareFunction. You can drill down into the objects properties inside the function.
Conceptually something like this:
public function aSort(a:Object, b:Object, fields:Array = null):int{
if(a.aa is b.aa){
return 0
} else if(a.aa > b.aa) {
return 1
} else{
return -1
}
}
When you create your sort object, you can specify the compare function:
var dataSort:Sort = new Sort();
dataSort.compareFunction = aSort;
arrayCollection.sort = dataSort;
arrayCollection.refresh();
Sort also has another property, compareFunction.

In AS3/Flex, how can I get from flat data to hierarchical data?

I have some data that gets pulled out of a database and mapped to an arraycollection. This data has a field called parentid, and I would like to map the data into a new arraycollection with hierarchical information to then feed to an advanced data grid.
I think I'm basically trying to take the parent object, add a new property/field/variable of type ArrayCollection called children and then remove the child object from the original list and clone it into the children array? Any help would be greatly appreciated, and I apologize ahead of time for this code:
private function PutChildrenWithParents(accountData : ArrayCollection) : ArrayCollection{
var pos_inner:int = 0;
var pos_outer:int = 0;
while(pos_outer < accountData.length){
if (accountData[pos_outer].ParentId != null){
pos_inner = 0;
while(pos_inner < accountData.length){
if (accountData[pos_inner].Id == accountData[pos_outer].ParentId){
accountData.addItemAt(
accountData[pos_inner] + {children:new ArrayCollection(accountData[pos_outer])},
pos_inner
);
accountData.removeItemAt(pos_outer);
accountData.removeItemAt(pos_inner+1);
}
pos_inner++;
}
}
pos_outer++;
}
return accountData;
}
I had a similar problem with a hierarchical task set which was slightly different as it has many root elements, this is what i did, seems good to me:
public static function convertFlatTasks(tasks:Array):Array
{
var rootItems:Array = [];
var task:TaskData;
// hashify tasks on id and clear all pre existing children
var taskIdHash:Array = [];
for each (task in tasks){
taskIdHash[task.id] = task;
task.children = [];
task.originalChildren = [];
}
// loop through all tasks and push items into their parent
for each (task in tasks){
var parent:TaskData = taskIdHash[task.parentId];
// if no parent then root element, i.e push into the return Array
if (parent == null){
rootItems.push(task);
}
// if has parent push into children and originalChildren
else {
parent.children.push(task);
parent.originalChildren.push(task);
}
}
return rootItems;
}
Try this:
AccountData:
public class AccountData
{
public var Id:int;
public var ParentId:int;
public var children:/*AccountData*/Array;
public function AccountData(id:int, parentId:int)
{
children = [];
this.Id = id;
this.ParentId = parentId;
}
}
Code:
private function PutChildrenWithParents(accountData:ArrayCollection):AccountData
{
// dummy data for testing
//var arr:/*AccountData*/Array = [new AccountData(2, 1),
// new AccountData(1, 0), // root
// new AccountData(4, 2),
// new AccountData(3, 1)
// ];
var arr:/*AccountData*/Array = accountData.source;
var dict:Object = { };
var i:int;
// generate a lookup dictionary
for (i = 0; i < arr.length; i++)
{
dict[arr[i].Id] = arr[i];
}
// root element
dict[0] = new AccountData(0, 0);
// generate the tree
for (i = 0; i < arr.length; i++)
{
dict[arr[i].ParentId].children.push(arr[i]);
}
return dict[0];
}
dict[0] holds now your root element.
Maybe it's doesn't have the best possible performance but it does what you want.
PS: This code supposes that there are no invalid ParentId's.
Here's what I ended up doing, apparently you can dynamically add new properties to an object with
object['new_prop'] = whatever
From there, I used a recursive function to iterate through any children so you could have n levels of the hierarchy and if it found anything it would pass up through the chain by reference until the original function found it and acted on it.
private function PutChildrenWithParents(accountData : ArrayCollection) : ArrayCollection{
var pos_inner:int = 0;
var pos_outer:int = 0;
var result:Object = new Object();
while(pos_outer < accountData.length){
if (accountData[pos_outer].ParentId != null){
pos_inner = 0;
while(pos_inner < accountData.length){
result = CheckForParent(accountData[pos_inner],
accountData[pos_outer].ParentId);
if ( result != null ){
if(result.hasOwnProperty('children') == false){
result['children'] = new ArrayCollection();
}
result.children.addItem(accountData[pos_outer]);
accountData.removeItemAt(pos_outer);
pos_inner--;
}
pos_inner++;
}
}
pos_outer++;
}
return accountData;
}
private function CheckForParent(suspectedParent:Object, parentId:String) : Object{
var parentObj:Object;
var counter:int = 0;
if ( suspectedParent.hasOwnProperty('children') == true ){
while (counter < suspectedParent.children.length){
parentObj = CheckForParent(suspectedParent.children[counter], parentId);
if (parentObj != null){
return parentObj;
}
counter++;
}
}
if ( suspectedParent.Id == parentId ){
return suspectedParent;
}
return null;
}

Doubt in action script for Flex: getting unique elements from an ArrayCollection

I have an ArrayCollection as mentioned below.
private var initDG:ArrayCollection = new ArrayCollection([
{fact: "Order #2314", appName: "AA"},
{fact: "Order #2315", appName: "BB"}
{fact: "Order #2316", appName: "BB"}
...
{fact: "Order #2320", appName: "CC"}
{fact: "Order #2321", appName: "CC"}
]);
I want to populate a ComboBox with UNIQUE VALUES of "appName" field from the ArrayCollection initDG.
<mx:ComboBox id="appCombo" dataProvider="{initDG}" labelField="appName"/>
One method I could think is to loop through the Array objects and for each object check and push unique appName entries into another Array. Is there any better solution available?
That sounds good to me:
var unique:Object = {};
var value:String;
var array:Array = initDG.toArray();
var result:Array = [];
var i:int = 0;
var n:int = array.length;
for (i; i < n; i++)
{
value = array[i].appName;
if (!unique[value])
{
unique[value] = true;
result.push(value);
}
}
return new ArrayCollection(result);
You can used this class for finding unique arraycollection:
tempArray=_uniqueArray.applyUnqiueKey1(_normalsearchdata.toArray());
"uniqueArray" this is package name and _normalsearchdata is ArrayCollection;
package{
import mx.collections.ArrayCollection;
public class applyUniqueKey{
private var tempArray:Array;
private var tempIndex = 0;
public var temp:String;
public function applyUnqiueKey1(myArray)
{
tempArray = new Array();
tempIndex = 0;
myArray.sort();
tempArray[0] = myArray[0];
tempIndex++;
for(var i=1; i<myArray.length; i++) {
if(myArray[i] != myArray[i-1]) {
tempArray[tempIndex] = myArray[i];
tempIndex++;
}
}
var temp=String(tempArray.join());
return new ArrayCollection(tempArray);
}
}
}
Alas, there is no unique() method in ActionScript's Array, but you can approximate it like this:
var names:Array = initDG.toArray().map(
function (e:Object, i:Number, a:Array):String {
return e.appName;
}
);
var uniqueNames:Array = names.filter(
function (name:String, i:Number, a:Array):Boolean {
// Only returns true for the first instance.
return names.indexOf(name) == i;
}
);
Note this happens to work because you are filtering strings, which are compared by value. This wouldn't be effective if you needed to filter arbitrary objects.

How can i make a reusable labelFunction for Flex Datagrid?

I have a label function like :
private function formatDate (item:Object, column:DataGridColumn):String
{
var df:DateFormatter = new DateFormatter();
df.formatString = "MM/DD/YY";
if (column.dataField == "startDate") {
return df.format(item.startDate);
}
return "ERR";
}
Which I use in a datacolumn by using labelFunction.
This works just fine if my data field is called 'startDate'. I want to make this function generic so I can use it everywhere.
How can I do this. i think i need to use some kind of 'reflection' - or perhaps another approach altogether?
You can make the function generic using the dataField attribute of the column as the key into your item.
private function formatDate (item:Object, column:DataGridColumn):String
{
var df:DateFormatter = new DateFormatter();
df.formatString = "MM/DD/YY";
var value:object = item[column.dataField];
return df.format(value);
}
-Ben
You can define another function, let's call it partial that binds some extra arguments to your function:
function partial( func : Function, ...boundArgs ) : Function {
return function( ...dynamicArgs ) : * {
return func.apply(null, boundArgs.concat(dynamicArgs))
}
}
Then you change your function like this:
private function formatDate( dataField : String, item : Object, column : DataGridColumn ) : String {
var df : DateFormatter = new DateFormatter();
df.formatString = "MM/DD/YY";
if ( column.dataField == dataField ) {
return df.format(item[dataField]);
}
return "ERR";
}
Notice that I have added a new argument called dataField first in the argument list, and replaced all references to "startDate" with that argument.
And use it like this:
var startDateLabelFunction : Function = partial(formatDate, "startDate");
var endDateLabelFunction : Function = partial(formatDate, "endDate");
The partial function returns a new function that calls the original function with the parameters from the call to partial concatenated with the parameters to the new function... you with me? Another way of putting it is that it can return a new function where N of the arguments are pre-bound to specific values.
Let's go through it step by step:
partial(formatDate, "startDate") returns a function that looks like this:
function( ...dynamicArgs ) : * {
return func.apply(null, boundArgs.concat(dynamicArgs));
}
but the func and boundArgs are what you passed as arguments to partial, so you could say that it looks like this:
function( ...dynamicArgs ) : * {
return formatDate.apply(null, ["startDate"].concat(dynamicArgs));
}
which, when it is called, will be more or less the same as this
function( item : Object, column : DataGridColumn ) : * {
return formatDate("startDate", item, column);
}
Tada!
here is more generic way:
public static function getDateLabelFunction(dateFormatString:String=null, mxFunction:Boolean = false) : Function {
var retf:Function;
// defaults
if(dateFormatString == null) dateFormatString = "MM/DD/YY";
if(mxFunction) {
retf = function (item:Object, column:DataGridColumn):String
{
var df:DateFormatter = new DateFormatter();
df.formatString = dateFormatString;
var value:Object = item[column.dataField];
return df.format(value);
}
}else {
retf = function (item:Object, column:GridColumn):String
{
var df:DateFormatter = new DateFormatter();
df.formatString = dateFormatString;
var value:Object = item[column.dataField];
return df.format(new Date(value));
}
}
return retf;
}
Usage (Spark DataGrid)
var labelFunction = getDateLabelFunction();
or for MX Datagrid
var labelFunction = getDateLabelFunction(null,true);
to pass custom Date Format String:
var labelFunction = getDateLabelFunction("DD/MM/YYYY",true);
Default is a "MM/DD/YYYY";

Resources