【问题标题】:Merging and reordering two arrays of objects合并和重新排序两个对象数组
【发布时间】:2015-06-13 07:39:48
【问题描述】:

我试图弄清楚如何合并两个对象数组。这是我需要做的:

  • field 属性是每个对象的唯一标识符
  • 输出只需要包含originalArray 中列出的对象
  • 需要维护localStorageArray 的顺序,注意之前的要求(顺序应为:barfoobaz
  • 输出需要包含来自localStorageArray 的以下属性值:hiddenwidthfield 是一个让步,因为它是标识符)
  • originalArray 的所有其他属性都需要在输出中维护

这是我的怪癖:

outputArray.forEach(function(originalItem){
    localStorageArray.forEach(function(localItem){
        if(originalItem.field === localItem.field){
            originalItem.hidden = localItem.hidden;
            originalItem.width = localItem.width;
        }
    });
});  

Full JS Fiddle

我能够获得所有属性,但我对如何根据localStorageArray 重新排序有点迷茫。我最初想在上一组 .forEach() 函数中这样做,但后来我想不要弄乱 forEach 循环中的顺序,因为我认为这可能会搞砸一些事情。

对我的解决方案有什么建议吗?

这是我的数组:

var originalArray = [{
        field: "foo",
        hidden: true,
        sortable: false,
        template: "<div>#=text#</div>",
        width: "20px",
        propA: "a",
        propB: "b"
    }, {
        field: "bar",
        hidden: false,
        sortable: false,
        template: "",
        width: "20%",
        propC: "C"
    }, {
        field: "baz",
        hidden: false,
        sortable: true,
        template: "<span>#=text#</span>",
        int: 3
    }];

var localStorageArray = [{
        field: "bar",
        hidden: false,
        sortable: false,
        width: "100px"
    }, {
        field: "foo",
        hidden: true,
        sortable: false,
        template: "<div>#=text#</div>",
        width: "40px"
    }, {
        field: "boo",
        hidden: true,
        sortable: true,
        template: "<div>Boo: #=text#</div>",
        width: "200px"
    }, {
        field: "baz",
        hidden: true,
        template: "baz:#=text#",
        width: "20px"
    }];

这是我想要的输出:

var desiredArray = [{
        field: "bar",
        hidden: false,
        sortable: false,
        template: "",
        width: "100px",
        propC: "C"
    }, {
        field: "foo",
        hidden: true,
        sortable: false,
        template: "<div>#=text#</div>",
        width: "40px",
        propA: "a",
        propB: "b"
    }, {
        field: "baz",
        hidden: true,
        sortable: true,
        template: "<span>#=text#</span>",
        width: "20px",
        int: 3
    }];

【问题讨论】:

  • 你能给出一些示例输入数据和预期输出吗?
  • 你说All other properties of originalArray need to be maintained in output 但在你摆弄desiredArray 包含{field: "foo", hidden: true, sortable: false, template: "&lt;div&gt;#=text#&lt;/div&gt;", width: "40px", propA: "a", propB: "b"},originalArray 具有width: "20px" 的值?
  • @Xotic750 - 正如我的要求所说,我想保留hiddenwidthlocalStorageArraywidth: "40px" 来自 localStorageArray 所以我的输出确实是正确的。
  • 因为天是关键的,你有没有考虑过使用对象哈希而不是数组?
  • @martypdx 我没有,但我无法控制这些对象的结构。

标签: javascript arrays object javascript-objects


【解决方案1】:

这是一个使用 ES6 方法的示例。

/*global document, console, expect */
(function () {
    'use strict';

    var originalArray = [{
            field: 'foo',
            hidden: true,
            sortable: false,
            template: '<div>#=text#</div>',
            width: '20px',
            propA: 'a',
            propB: 'b'
        }, {
            field: 'bar',
            hidden: false,
            sortable: false,
            template: '',
            width: '20%',
            propC: 'C'
        }, {
            field: 'baz',
            hidden: false,
            sortable: true,
            template: '<span>#=text#</span>',
            int: 3
        }],
        localStorageArray = [{
            field: 'bar',
            hidden: false,
            sortable: false,
            width: '100px'
        }, {
            field: 'foo',
            hidden: true,
            sortable: false,
            template: '<div>#=text#</div>',
            width: '40px'
        }, {
            field: 'boo',
            hidden: true,
            sortable: true,
            template: '<div>Boo: #=text#</div>',
            width: '200px'
        }, {
            field: 'baz',
            hidden: true,
            template: 'baz:#=text#',
            width: '20px'
        }],
        desiredArray = [{
            field: 'bar',
            hidden: false,
            sortable: false,
            template: '',
            width: '100px',
            propC: 'C'
        }, {
            field: 'foo',
            hidden: true,
            sortable: false,
            template: '<div>#=text#</div>',
            width: '40px',
            propA: 'a',
            propB: 'b'
        }, {
            field: 'baz',
            hidden: true,
            sortable: true,
            template: '<span>#=text#</span>',
            width: '20px',
            int: 3
        }],
        outputArray = [],
        pre = document.getElementById('out'),
        equalField = function (originalElement) {
            return originalElement.field === this.field;
        };

    localStorageArray.reduce(function (acc, localElement) {
        var original = originalArray.find(equalField, localElement),
            shallowCopy;

        if (original) {
            shallowCopy = Object.assign({}, original);
            shallowCopy.hidden = localElement.hidden;
            shallowCopy.width = localElement.width;
            acc.push(shallowCopy);
        }

        return acc;
    }, outputArray);

    try {
        expect(outputArray).to.eql(desiredArray);
        pre.textContent = 'outputArray is equal to desiredArray\n\n';
    } catch (e) {
        console.error(e);
        pre.textContent = 'outputArray is not equal to desiredArray\n\n';
    }

    pre.textContent += JSON.stringify(outputArray, null, 2);
}());
<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.32.0/es6-shim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/expect.js/0.2.0/expect.min.js"></script>
<pre id="out"></pre>

更新:根据您的新评论和数据,这可能是一个解决方案。

var originalArray = [{
        field: "foo",
        hidden: true,
        sortable: false,
        template: "<div>#=text#</div>",
        width: "20px",
        propA: "a",
        propB: "b"
    }, {
        field: "bee",
        hidden: true,
        sortable: false,
        template: "=#text#",
        int: 4
    }, {
        field: "bar",
        hidden: false,
        sortable: false,
        template: "",
        width: "20%",
        propC: "C"
    }, {
        field: "baz",
        hidden: false,
        sortable: true,
        template: "<span>#=text#</span>",
        int: 3
    }],
    localStorageArray = [{
        field: "bar",
        hidden: false,
        sortable: false,
        width: "100px"
    }, {
        field: "foo",
        hidden: true,
        sortable: false,
        template: "<div>#=text#</div>",
        width: "40px"
    }, {
        field: "boo",
        hidden: true,
        sortable: true,
        template: "<div>Boo: #=text#</div>",
        width: "200px"
    }, {
        field: "baz",
        hidden: true,
        template: "baz:#=text#",
        width: "20px"
    }],
    desiredArray = [{
        field: "bar",
        hidden: false,
        sortable: false,
        template: "",
        width: "100px",
        propC: "C"
    }, {
        field: "bee",
        hidden: true,
        sortable: false,
        template: "=#text#",
        int: 4
    }, {
        field: "foo",
        hidden: true,
        sortable: false,
        template: "<div>#=text#</div>",
        width: "40px",
        propA: "a",
        propB: "b"
    }, {
        field: "baz",
        hidden: true,
        sortable: true,
        template: "<span>#=text#</span>",
        width: "20px",
        int: 3
    }],
    outputArray = [],
    pre = document.getElementById('out'),
    equalField = function (originalElement) {
        return originalElement.field === this.field;
    };

localStorageArray.reduce(function (acc, localElement) {
    var original = originalArray.find(equalField, localElement),
        shallowCopy;

    if (original) {
        shallowCopy = Object.assign({}, original);
        shallowCopy.hidden = localElement.hidden;
        shallowCopy.width = localElement.width;
        acc.push(shallowCopy);
    }

    return acc;
}, outputArray);

originalArray.forEach(function (originalElement, index) {
    if (!this.find(equalField, originalElement)) {
        this.splice(index, 0, Object.assign({}, originalElement));
    }
}, outputArray);

try {
    expect(outputArray).to.eql(desiredArray);
    pre.textContent = 'outputArray is equal to desiredArray\n\n';
} catch (e) {
    console.error(e);
    pre.textContent = 'outputArray is not equal to desiredArray\n\n';
}

pre.textContent += JSON.stringify(outputArray, null, 2);
<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.32.0/es6-shim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/expect.js/0.2.0/expect.min.js"></script>
<pre id="out"></pre>

【讨论】:

  • 感谢您的解决方案,但它并不能满足我的所有要求,特别是保留来自 localStorageArrayhiddenwidth 值。 @Jyoti Puri 有一个非常简单有效的解决方案。
【解决方案2】:

检查这个小提琴:http://jsfiddle.net/j2u2hhk6/

实际上你可以这样做:

var outputArray = [];

localStorageArray.forEach(function(localItem){
    originalArray.forEach(function(originalItem){
        if(originalItem.field === localItem.field){
            var item = JSON.parse(JSON.stringify(originalItem));
            item.hidden = localItem.hidden;
            item.width = localItem.width;
            outputArray.push(item);
        }
    });
});

【讨论】:

  • 这太天才了,太简单了!当我在我的电脑上时,我会再次仔细检查。谢谢!
  • 请注意这是一个 O(n^2) 并且如果您的数组大小变大可能会表​​现不佳。在这种情况下,您可能希望首先对数组进行排序或使用哈希的某种组合来跟踪键。
  • 只需将第二个forEach 更改为some 并在匹配时返回true 即可获得一些效率。
  • 我确实发现了解决方案的一个问题,虽然我没有在我的问题中提到它:当我在originalArray 中有一个在outputArray 中不存在的对象时, newArray 中不包含新对象。有什么想法吗?:jsfiddle.net/fmpeyton/4k6djpum
  • 改变游戏规则的评论。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-04-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-08
相关资源
最近更新 更多