【问题标题】:Efficient way to filter an Object Array on a specific property?在特定属性上过滤对象数组的有效方法?
【发布时间】:2012-05-09 09:03:11
【问题描述】:

我有一个具有不同属性的对象数组,我需要以不重复特定属性的方式过滤数组。

例如:

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"}];

现在假设我要过滤属性baz 上的对象。过滤数组的最有效方法是什么,以便在操作后没有两个元素对baz 具有相同的值?

在我的示例中,结果应该只包含:

var result:Array = [{foo:"a1", bar:"b1", baz:"c1"},
                    {foo:"a2", bar:"b2", baz:"c2"},
                    {foo:"a3", bar:"b1", baz:"c3"}]

因为其他对象会有 baz 属性的重复条目。

结果数组的顺序并不重要,baz 值相同的对象中的哪个对象进入结果数组也不重要。


更新:

对象数组用作数据提供者,以使用有关聊天室的信息填充s:Datagrid。数组中的对象携带相关信息(如服务器上的房间 ID 和其他一些配置设置)。

我在示例中使用的baz 属性实际上是聊天室配置使用的语言的ID,我想创建一个s:DropDownList,我可以使用它来过滤Datagrid 的各个语言(例如显示所有使用“德语”的房间)。

很可能有许多对象具有相同的语言 ID,但我只希望每个语言 ID 在 DropDownList 中出现一次。

我需要从Datagrids's dataprovider(源数组)中提取该信息,并且无法直接检索我的语言,因为DropDownList 是通用DatagridHeaderRenderer 的一部分,该通用DatagridHeaderRenderer 用于许多不同的Datagrids数据。

【问题讨论】:

  • @wvxvw:它确实应该创建一个副本,因为不能修改源数组。我将在问题中添加带有更多信息的用例。源数组未按应过滤的属性排序,baz 重复相同值的机会很高。

标签: arrays actionscript-3 flash


【解决方案1】:
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 = {};
}

从多个来源修改,但主要是:http://blog.flexexamples.com/2007/08/05/removing-duplicate-items-from-an-array-using-the-arrayfilter-method/

或者您可以只使用 arrayCollection 及其内置的 filterFunction。见:http://cookbooks.adobe.com/post_Using_the_to_ArrayCollection_s_filterFunction-5441.html

【讨论】:

    【解决方案2】:

    从表面上看,它应该可以工作。使用 Array.filter 通常是循环执行相同操作的两倍。

    我认为'Dom 的 removeDupes 函数并不能完全满足要求,尽管它可能是一种更通用的方法(例如,如果 === 不是一个好的比较函数,那么这个给你一种扩展它的方法。)但是使用hasOwnPropery 是一个很大的禁忌。你永远不应该碰它——这个功能只存在于 ES 兼容性。否则是邪恶的 - 既是潜在的安全漏洞(因为它在Object.prototype 上定义,因此很容易被外来代码覆盖)并且速度很慢(出于同样的原因 - 原型上定义的函数的查找速度较慢那些在类中定义的)。

        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);
        }
    

    【讨论】:

    • 你说的一些东西对我来说似乎很奇怪...... hasOwnProperty() 是一个安全漏洞吗?真的吗?在某些情况下,例如在搜索 XML 时,该方法非常有用。另外, Array.filter() 比循环慢吗?上次我实际检查时,它比使用循环要快。而且,这就是那些原生方法的重点,对吧?我会自己用谷歌搜索,但如果你引用了特定的来源,我真的很想检查一下。
    • 使用数组原生函数我做了一些性能测试。但是,根据您的反馈,我会在下次需要优化时重新测试它们。我可能有一个极端情况,使用本机函数比我替换它的旧逻辑更快。我仍然看不到 hasOwnProperty() 的安全问题是什么,但我一般不了解安全性。不过,您提供给我的详细信息将帮助我搜索更多信息,非常感谢。
    • 顺便说一句,我仍然找不到任何在线讨论这个问题的东西。搜索“as3 hasownproperty security hole”实际上提出了这个线程。 :-)
    猜你喜欢
    • 1970-01-01
    • 2021-12-08
    • 2017-05-25
    • 2021-08-05
    • 2017-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多