밑줄을 사용하여 객체 배열을 어떻게 복제합니까?


82
#!/usr/bin/env node
var _ = require('underscore');
var a = [{f: 1}, {f:5}, {f:10}];
var b = _.clone(a);
b[1].f = 55;
console.log(JSON.stringify(a));

결과는 다음과 같습니다.

[{"f":1},{"f":55},{"f":10}]

클론이 작동하지 않는 것 같습니다! 그래서 나는 RTFM, 그리고 이것을보십시오 :

http://underscorejs.org/#clone

개체의 얕은 복사 된 복제본을 만듭니다. 중첩 된 개체 또는 배열은 복제되지 않고 참조로 복사됩니다.

그래서 _.clone꽤 쓸모가 없습니다. 실제로 객체 배열을 복사하는 방법이 있습니까?


4
거부 된 딥 카피에 대한 pull 요청이있었습니다. github.com/jashkenas/underscore/pull/595 Lo-Dash에는 cloneDeep이 있습니다
epascarello

5
lol 나는 밑줄에 말장난을 깨달았다. 낮은 대시.
Jess

답변:


122

음, 속임수가 있습니다! clone이 중첩 된 개체를 "복제"하지 않는 경우 맵 호출 내에서 각 개체를 명시 적으로 복제하여 강제로 실행할 수 있습니다. 이렇게 :

#!/usr/bin/env node
var _ = require('underscore');
var a = [{f: 1}, {f:5}, {f:10}];
var b = _.map(a, _.clone);       // <----
b[1].f = 55;
console.log(JSON.stringify(a));

인쇄물:

[{"f":1},{"f":5},{"f":10}]

예이! a변경되지 않았습니다! 이제 b원하는대로 편집 할 수 있습니다 !


48
하지만 조심하세요. 물론 이것은 두 단계 깊이에서만 작동합니다. 이 예제보다 더 많이 중첩 된 배열이나 객체에는 적합하지 않습니다.
Simon Zyx

1
디자인으로 밑줄도 제대로하지 복제 정규식 또는 날짜 값을 것이다
마크 K 코완

1
여기를 찾는 사람은 아래 내 대답을 볼 수 있습니다.
gdibble

65

모든 수준의 중첩 데이터에서 작동하며 밑줄이 필요하지 않은 Github의 문제에서 추출한 또 다른 솔루션 :

JSON.parse(JSON.stringify(obj))

13
이것은 개체에주기가 없으면 작동 JSON.stringify하며이 경우 오류가 발생합니다. 원래의 경우는 아니지만 여전히 흥미로운 상황입니다. a = {simple: 'thing'}; a.cycle = a ; JSON.stringify(a).
mcdave

10
또한이 솔루션은 단순한 유형의 객체에만 작동합니다. 예를 들어, 객체가 Date또는 Regex인스턴스 인 경우 문자열로 직렬화됩니다. 세상의 끝은 아니지만 이것을 사용하고 Date인스턴스를 기대한다면 처리해야 합니다.
cayleyh

1
그리고 누군가가 undefined이것을 시도하고 먹이 를 줄 것이라고 생각 하면 원하지 JSON.parse(JSON.stringify(obj) || null)않을 것입니다. 그렇지 않으면 오류가 발생합니다.
이안 맥키

1
@cayleyh가 언급 한 것과 함께 이것은 function모두 삭제 됩니다.
Marko Grešak

13

FWIW, lodash에는 cloneDeep 함수가 있습니다.

이 메서드는 값을 재귀 적으로 복제한다는 점을 제외하면 _.clone과 같습니다.


9

밑줄 API 참조 :

_.toArray(list)목록에서 실제 배열을 만듭니다 (반복 할 수있는 모든 항목). arguments 객체를 변환하는 데 유용합니다.

... 또는이 경우 어레이 복제 . 이 시도:

var _ = require('underscore');
var array1 =  [{a:{b:{c:1}}},{b:{c:{a:2}}},{c:{a:{b:3}}}];
var array2 = _.toArray(array1);
console.log(array1 === array2); --> false
console.log(array1[0] === array2[0]); --> true

다음은 -thx 아래에 Steve의 의견 후에 만든 부록입니다.

Vanilla JS (또는 _.clone원하는 경우 사용 ) 딥 클로닝 재귀 도우미 :

function clone(thing, opts) {
    var newObject = {};
    if (thing instanceof Array) {
        return thing.map(function (i) { return clone(i, opts); });
    } else if (thing instanceof Date) {
        return new Date(thing);
    } else if (thing instanceof RegExp) {
        return new RegExp(thing);
    } else if (thing instanceof Function) {
        return opts && opts.newFns ?
                   new Function('return ' + thing.toString())() :
                   thing;
    } else if (thing instanceof Object) {
        Object.keys(thing).forEach(function (key) {
            newObject[key] = clone(thing[key], opts);
        });
        return newObject;
    } else if ([ undefined, null ].indexOf(thing) > -1) {
        return thing;
    } else {
        if (thing.constructor.name === 'Symbol') {
            return Symbol(thing.toString()
                       .replace(/^Symbol\(/, '')
                       .slice(0, -1));
        }
        // return _.clone(thing);  // If you must use _ ;)
        return thing.__proto__.constructor(thing);
    }
}

var a = {
    a: undefined,
    b: null,
    c: 'a',
    d: 0,
    e: Symbol('a'),
    f: {},
    g: { a:1 },
    h: [],
    i: [ { a:2 }, { a:3 } ],
    j: [ 1, 2 ],
    k: function (a) { return a; },
    l: /[a-z]/g,
    z: [ {
        a: undefined,
        b: null,
        c: 'b',
        d: 1,
        e: Symbol(1),
        f: {},
        g: { b:2 },
        h: { c:{ c:3 } },
        i: { a:Symbol('b') },
        j: { a:undefined, b:null },
        k: [],
        l: [ 1, [ 1, 2 ], [ [ 1, 2, 3 ] ] ],
        m: function (a) { return !a; },
        n: { a:function (a) { return !!a; } },
        o: /(a|b)/i
       } ]
};
var b = clone(a);
var c = clone(a, { newFns:true });


/* Results - value beneath each for reference:

a.a === b.a --> true
undefined

a.b === b.b --> true
null

a.c === b.c --> true
'a'

a.d === b.d --> true
0

a.e === b.e --> false
Symbol(a)

a.f === b.f --> false
{}

a.g === b.g --> false
{ a:1 }

a.h === b.h --> false
[]

a.i === b.i --> false
[ { a:2 }, { a:3 } ]

a.i[0] === b.i[0] --> false
{ a:2 }

a.i[0].a === b.i[0].a --> true
2

a.j === b.j --> false
[ 1, 2 ]

a.k === b.k --> true
a.k === c.k --> false
function (a) { return a; }

a.l === b.l --> false
/[a-z]/g

a.z === b.z --> false
[Object]

a.z[0].a === b.z[0].a --> true
undefined

a.z[0].b === b.z[0].b --> true
null

a.z[0].c === b.z[0].c --> true
'b'

a.z[0].d === b.z[0].d --> true
1

a.z[0].e === b.z[0].e --> 
false
Symbol(1)

a.z[0].f === b.z[0].f --> false
{}

a.z[0].g === b.z[0].g -- > false
{ b:2 }

a.z[0].g.b === b.z[0].g.b --> true
2

a.z[0].h === b.z[0].h --> false
{ c:{ c:3 } }

a.z[0].h.c === b.z[0].h.c --> false
{ c:3 }

a.z[0].h.c.c === b.z[0].h.c.c --> true
3

a.z[0].i === b.z[0].i --> false
{ a:Symbol(b) }

a.z[0].i.a === b.z[0].i.a --> false
Symbol(b)

a.z[0].j === b.z[0].j --> false
{ a:undefined, b:null }

a.z[0].j.a === b.z[0].j.a --> true
undefined

a.z[0].k === b.z[0].k --> false
[]

a.z[0].l === b.z[0].l --> false
[ 1, [ 1, 2 ], [ [ 1, 2, 3 ] ] ]

a.z[0].l[1] === b.z[0].l[1] --> false
[ 1, 2 ]

a.z[0].l[1][1] === b.z[0].l[1][1] --> true
2

a.z[0].m === b.z[0].m --> true
a.z[0].m === c.z[0].m --> false
function (a) { return !a; }

a.z[0].n === b.z[0].n --> false
{ a:function (a) { return !!a; } }

a.z[0].n.a === b.z[0].n.a --> true
a.z[0].n.a === c.z[0].n.a --> false
function (a) { return !!a; }

a.z[0].o === b.z[0].o --> false
/(a|b)/i

*/

이것이 최고의 답변입니다.
Pierre

_.toArray(list)배열의 개체를 복제하지 않습니다. var array1 = [{a: 1}, {a: 2}, {a: 3}]; var array2 = _.toArray(array1); array2[0].a = 999; console.log(array1[0]); --> {a: 999}
Steve Lang

@SteveLang을 지적 해 주셔서 감사합니다. 죄송합니다. 따라서 위의 바닐라 JS fn을 만드는 데 약간의 시간이 걸렸습니다. 사용자가 실제로 조건 _.clone에서 사용할 수 있어야하는 경우 else;)
gdibble
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.