Programing

JavaScript로 객체 배열에서 고유 한 값을 얻는 방법은 무엇입니까?

lottogame 2020. 3. 18. 07:59
반응형

JavaScript로 객체 배열에서 고유 한 값을 얻는 방법은 무엇입니까?


내가 다음을 가정합니다.

var array = 
    [
        {"name":"Joe", "age":17}, 
        {"name":"Bob", "age":17}, 
        {"name":"Carl", "age": 35}
    ]

결과 배열을 얻을 수 있도록 모든 연령대를 배열 할 수있는 가장 좋은 방법은 무엇입니까?

[17, 35]

대안으로 데이터 또는 더 나은 방법을 구성하여 "age"값을 확인하는 각 배열을 반복하고 다른 배열의 존재 여부를 확인하고 그렇지 않은 경우 추가 할 수있는 방법이 있습니까?

어떤 방법이 있다면 반복하지 않고 독특한 연령대를 끌어낼 수 있습니다 ...

현재 비효율적 인 방법으로 개선하고 싶습니다 ... "어레이"대신 객체의 배열이지만 일부 고유 키 (예 : "1,2,3")가있는 객체의 "맵"인 경우 그래요 가장 효율적인 방법을 찾고 있습니다.

다음은 현재 내가하는 방법이지만, 반복 작업은 효과가 있지만 효율성이 어려워 보입니다.

var distinct = []
for (var i = 0; i < array.length; i++)
   if (array[i].age not in distinct)
      distinct.push(array[i].age)

이것이 PHP라면 키로 배열을 만들고 array_keys마지막에 가져갈 것이지만 JS에는 그런 사치가 없습니다. 대신 이것을 시도하십시오 :

var flags = [], output = [], l = array.length, i;
for( i=0; i<l; i++) {
    if( flags[array[i].age]) continue;
    flags[array[i].age] = true;
    output.push(array[i].age);
}

ES6 / ES2015 이상을 사용하는 경우 다음과 같이 할 수 있습니다.

const unique = [...new Set(array.map(item => item.age))];

방법은 다음과 같습니다 .


ES6 사용

let array = [
  { "name": "Joe", "age": 17 },
  { "name": "Bob", "age": 17 },
  { "name": "Carl", "age": 35 }
];
array.map(item => item.age)
  .filter((value, index, self) => self.indexOf(value) === index)

> [17, 35]

이와 같은 사전 접근 방식을 사용할 수 있습니다. 기본적으로 사전에서 키로 구별하려는 값을 지정합니다. 키가 존재하지 않으면 해당 값을 고유 한 값으로 추가하십시오.

var unique = {};
var distinct = [];
for( var i in array ){
 if( typeof(unique[array[i].age]) == "undefined"){
  distinct.push(array[i].age);
 }
 unique[array[i].age] = 0;
}

실제 데모는 다음과 같습니다. http://jsfiddle.net/jbUKP/1

이것은 O (n)입니다. 여기서 n은 배열의 객체 수이고 m은 고유 값의 수입니다. 각 값을 한 번 이상 검사해야하므로 O (n)보다 빠른 방법은 없습니다.

공연

http://jsperf.com/filter-versus-dictionary 이 사전을 실행할 때 30 % 더 빨랐습니다.


2017 년 8 월 25 일 현재 Typescript 용 ES6를 통한 새로운 세트를 사용하여이 문제를 해결하는 방법입니다.

Array.from(new Set(yourArray.map((item: any) => item.id)))

ES6 기능을 사용하면 다음과 같은 작업을 수행 할 수 있습니다.

const uniqueAges = [...new Set( array.map(obj => obj.age)) ];

Dups를 매핑하고 제거합니다.

var ages = array.map(function(obj) { return obj.age; });
ages = ages.filter(function(v,i) { return ages.indexOf(v) == i; });

console.log(ages); //=> [17, 35]

편집 : Aight! 성능면에서 가장 효율적인 방법은 아니지만 가장 읽기 쉬운 IMO입니다. 마이크로 최적화에 관심이 있거나 엄청난 양의 데이터가 있다면 규칙적인 for루프가 더 "효율적"입니다.


var unique = array
    .map(p => p.age)
    .filter((age, index, arr) => arr.indexOf(age) == index)
    .sort(); // sorting is optional

// or in ES6

var unique = [...new Set(array.map(p => p.age))];

ES6 예

var data = [
  { name: "Joe", age: 17}, 
  { name: "Bob", age: 17}, 
  { name: "Carl", age: 35}
];

const arr = data.map(p => p.age); // [17, 17, 35]
const s = new Set(arr); // {17, 35} a set removes duplications
const unique = [...s]; // [17, 35] Use the spread operator to transform a set into an Array
// or
const unique2 = Array.from(s); // [17, 35]

forEach(최신 브라우저 및 노드 JS 세계에 도움) @ 트래비스-J의 답변 버전 :

var unique = {};
var distinct = [];
array.forEach(function (x) {
  if (!unique[x.age]) {
    distinct.push(x.age);
    unique[x.age] = true;
  }
});

Chrome v29.0.1547에서 34 % 빨라짐 : http://jsperf.com/filter-versus-dictionary/3

그리고 매퍼 기능을 사용하는 일반적인 솔루션 (직접 맵보다 느리지 만 예상되는 결과) :

function uniqueBy(arr, fn) {
  var unique = {};
  var distinct = [];
  arr.forEach(function (x) {
    var key = fn(x);
    if (!unique[key]) {
      distinct.push(key);
      unique[key] = true;
    }
  });
  return distinct;
}

// usage
uniqueBy(array, function(x){return x.age;}); // outputs [17, 35]

이미 유효한 답변이 많이 있지만 reduce()깨끗하고 단순하기 때문에 메소드 만 사용하는 답변을 추가하고 싶었습니다 .

function uniqueBy(arr, prop){
  return arr.reduce((a, d) => {
    if (!a.includes(d[prop])) { a.push(d[prop]); }
    return a;
  }, []);
}

다음과 같이 사용하십시오.

var array = [
  {"name": "Joe", "age": 17}, 
  {"name": "Bob", "age": 17}, 
  {"name": "Carl", "age": 35}
];

var ages = uniqueBy(array, "age");
console.log(ages); // [17, 35]

기본적으로 모든 새 프로젝트에서 Underscore고수하기 시작 했기 때문에 이러한 작은 데이터 전송 문제에 대해 생각할 필요가 없습니다.

var array = [{"name":"Joe", "age":17}, {"name":"Bob", "age":17}, {"name":"Carl", "age": 35}];
console.log(_.chain(array).map(function(item) { return item.age }).uniq().value());

생산 [17, 35]합니다.


lodash를 사용하여

var array = [
    { "name": "Joe", "age": 17 },
    { "name": "Bob", "age": 17 },
    { "name": "Carl", "age": 35 }
];
_.chain(array).pluck('age').unique().value();
> [17, 35]

이 문제를 해결하는 또 다른 방법이 있습니다.

var result = {};
for(var i in array) {
    result[array[i].age] = null;
}
result = Object.keys(result);

이 솔루션이 다른 솔루션과 비교하여 얼마나 빠른지 모르겠지만 더 깨끗한 모양을 좋아합니다. ;-)


편집 : 좋아, 위의 모든 것 중에서 가장 느린 해결책 인 것 같습니다.

나는 여기에 성능 테스트 케이스를 만들었습니다 : http://jsperf.com/distinct-values-from-array

연령대 (정수)를 테스트하는 대신 이름 (문자열)을 비교하기로했습니다.

방법 1 (TS의 솔루션)은 매우 빠릅니다. 흥미롭게도 Method 7은 다른 모든 솔루션보다 뛰어납니다. 여기서는 .indexOf ()를 제거하고 루프 된 함수 호출을 피하면서 "수동"구현을 사용했습니다.

var result = [];
loop1: for (var i = 0; i < array.length; i++) {
    var name = array[i].name;
    for (var i2 = 0; i2 < result.length; i2++) {
        if (result[i2] == name) {
            continue loop1;
        }
    }
    result.push(name);
}

Safari와 Firefox를 사용한 성능의 차이는 놀랍습니다. 그리고 Chrome이 최적화에있어 최고의 역할을하는 것처럼 보입니다.

왜 위의 스 니펫이 다른 코드와 비교하여 그렇게 빠른지 잘 모르겠습니다. 어쩌면 나보다 현명한 사람이 대답 할 수 있습니다. ;-)


underscore.js _.uniq(_.pluck(array,"age"))


Lodash 사용

var array = [
    { "name": "Joe", "age": 17 },
    { "name": "Bob", "age": 17 },
    { "name": "Carl", "age": 35 }
];

_.chain(array).map('age').unique().value();

반환 [17,35]


function get_unique_values_from_array_object(array,property){
    var unique = {};
    var distinct = [];
    for( var i in array ){
       if( typeof(unique[array[i][property]]) == "undefined"){
          distinct.push(array[i]);
       }
       unique[array[i][property]] = 0;
    }
    return distinct;
}

groupBy 기능을 찾고 있다고 생각합니다 (Lodash 사용).

_personsList = [{"name":"Joe", "age":17}, 
                {"name":"Bob", "age":17}, 
                {"name":"Carl", "age": 35}];
_uniqAgeList = _.groupBy(_personsList,"age");
_uniqAges = Object.keys(_uniqAgeList);

결과를 생성합니다 :

17,35

jsFiddle 데모 : http://jsfiddle.net/4J2SX/201/


다음은 reduce를 사용하고 매핑을 허용하며 삽입 순서를 유지하는 다목적 솔루션입니다.

items : 배열

mapper : 항목을 기준에 매핑하거나 비워 두어 항목 자체를 매핑하는 단항 함수입니다.

function distinct(items, mapper) {
    if (!mapper) mapper = (item)=>item;
    return items.map(mapper).reduce((acc, item) => {
        if (acc.indexOf(item) === -1) acc.push(item);
        return acc;
    }, []);
}

용법

const distinctLastNames = distinct(items, (item)=>item.lastName);
const distinctItems = distinct(items);

이것을 배열 프로토 타입에 추가하고 스타일 인 경우 items 매개 변수를 생략 할 수 있습니다.

const distinctLastNames = items.distinct( (item)=>item.lastName) ) ;
const distinctItems = items.distinct() ;

배열 대신 Set을 사용하여 일치 속도를 높일 수도 있습니다.

function distinct(items, mapper) {
    if (!mapper) mapper = (item)=>item;
    return items.map(mapper).reduce((acc, item) => {
        acc.add(item);
        return acc;
    }, new Set());
}

const x = [
  {"id":"93","name":"CVAM_NGP_KW"},
  {"id":"94","name":"CVAM_NGP_PB"},
  {"id":"93","name":"CVAM_NGP_KW"},
  {"id":"94","name":"CVAM_NGP_PB"}
].reduce(
  (accumulator, current) => accumulator.some(x => x.id === current.id)? accumulator: [...accumulator, current ], []
)

console.log(x)

/* output 
[ 
  { id: '93', name: 'CVAM_NGP_KW' },
  { id: '94', name: 'CVAM_NGP_PB' } 
]
*/


방금 이것을 찾았고 유용하다고 생각했습니다.

_.map(_.indexBy(records, '_id'), function(obj){return obj})

밑줄을 다시 사용 하므로 이와 같은 객체가 있으면

var records = [{_id:1,name:'one', _id:2,name:'two', _id:1,name:'one'}]

그것은 당신에게 유일한 객체를 줄 것입니다.

여기서 일어나는 일은 이런 indexBy반환하는 것입니다

{ 1:{_id:1,name:'one'}, 2:{_id:2,name:'two'} }

지도이기 때문에 모든 키가 고유합니다.

그런 다음이 목록을 배열에 다시 매핑합니다.

고유 한 값만 필요한 경우

_.map(_.indexBy(records, '_id'), function(obj,key){return key})

(가) 있다는 사실을 숙지 key대신 정수를 필요로하는 경우, 당신이해야하므로 문자열로 반환

_.map(_.indexBy(records, '_id'), function(obj,key){return parseInt(key)})

Array.prototype.includes가 포함되어 있거나 polyfill 하려는 경우 다음 과 같이 작동합니다.

var ages = []; array.forEach(function(x) { if (!ages.includes(x.age)) ages.push(x.age); });

아래 코드는 나이가 고유하지 않은 새로운 배열뿐만 아니라 나이가 중복되지 않는 새로운 배열을 보여줍니다.

var data = [
  {"name": "Joe", "age": 17}, 
  {"name": "Bob", "age": 17}, 
  {"name": "Carl", "age": 35}
];

var unique = [];
var tempArr = [];
data.forEach((value, index) => {
    if (unique.indexOf(value.age) === -1) {
        unique.push(value.age);
    } else {
        tempArr.push(index);    
    }
});
tempArr.reverse();
tempArr.forEach(ele => {
    data.splice(ele, 1);
});
console.log('Unique Ages', unique);
console.log('Unique Array', data);```

나와 같은 속도를 유지하면서보다 "기능적인"기능을 선호하는 경우이 예에서는 축소 클로저 안에 래핑 된 빠른 사전 검색을 사용합니다.

var array = 
[
    {"name":"Joe", "age":17}, 
    {"name":"Bob", "age":17}, 
    {"name":"Carl", "age": 35}
]
var uniqueAges = array.reduce((p,c,i,a) => {
    if(!p[0][c.age]) {
        p[1].push(p[0][c.age] = c.age);
    }
    if(i<a.length-1) {
        return p
    } else {
        return p[1]
    }
}, [{},[]])

테스트 에 따르면 내 솔루션은 제안 된 답변보다 두 배 빠릅니다.


unique(obj, prop) {
    let result = [];
    let seen = new Set();

    Object.keys(obj)
        .forEach((key) => {
            let value = obj[key];

            let test = !prop
                ? value
                : value[prop];

            !seen.has(test)
                && seen.add(test)
                && result.push(value);
        });

    return result;
}

타이프 스크립트에 강력한 형식의 쿼리 가능한 컬렉션 을 제공하는 라이브러리가 있습니다 .

컬렉션은 다음과 같습니다

  • 명부
  • 사전

라이브러리를 ts-generic-collections 라고 합니다.

GitHub의 소스 코드 :

https://github.com/VeritasSoftware/ts-generic-collections

아래와 같이 고유 한 값을 얻을 수 있습니다

  it('distinct', () => {
    let numbers: number[] = [1, 2, 3, 1, 3];
    let list = new List(numbers);

    let distinct = list.distinct(new EqualityComparer());

    expect(distinct.length == 3);
    expect(distinct.elementAt(0) == 1);
    expect(distinct.elementAt(1) == 2);
    expect(distinct.elementAt(2) == 3);
  });

  class EqualityComparer implements IEqualityComparer<number> {
    equals(x: number, y: number) : boolean {
      return x == y;
    }
  }

새로운 Ecma 기능을 사용하는 것은 좋지만 모든 사용자가 아직 사용할 수있는 것은 아닙니다.

다음 코드는 글로벌 배열 객체에 distinct 라는 새 함수를 첨부 합니다. 객체 배열의 고유 한 값을 얻으려고하면 값의 이름을 전달하여 해당 유형의 고유 한 값을 얻을 수 있습니다.

Array.prototype.distinct = function(item){   var results = [];
for (var i = 0, l = this.length; i < l; i++)
    if (!item){
        if (results.indexOf(this[i]) === -1)
            results.push(this[i]);
        } else {
        if (results.indexOf(this[i][item]) === -1)
            results.push(this[i][item]);
    }
return results;};

데모를 위해 CodePen에서 내 게시물확인하십시오 .


단지 시도

var x = [] ;
for (var i = 0 ; i < array.length ; i++)
{
 if(x.indexOf(array[i]['age']) == -1)
  {
    x.push(array[i]['age']);
  }
}
console.log(x);

뛰어난 성능의 단순한 원 라이너. 테스트 에서 ES6 솔루션보다 6 % 빠릅니다 .

var ages = array.map (function (o) {return o.age}). filter (function (v, i, a) {return a.indexOf (v) === i});


나는 이것이 오래되고 상대적으로 잘 대답 된 질문이라는 것을 알고 있으며 내가주는 대답은 완전한 대상을 되 찾을 것입니다 (이 게시물에 대한 많은 의견에서 제안 된 것으로 보임). "끈적"일 수도 있지만 가독성 측면에서 다른 많은 솔루션보다 훨씬 깨끗합니다 (효율적이지는 않지만).

이것은 배열 내부의 완전한 객체의 고유 한 배열을 반환합니다.

let productIds = data.map(d => { 
   return JSON.stringify({ 
      id    : d.sku.product.productId,
      name  : d.sku.product.name,
      price : `${d.sku.product.price.currency} ${(d.sku.product.price.gross / d.sku.product.price.divisor).toFixed(2)}`
   })
})
productIds = [ ...new Set(productIds)].map(d => JSON.parse(d))```

나는 Kotlin에서와 같은 일반적인 경우를 위해 TypeScript에서 내 자신을 썼습니다 Array.distinctBy {}...

function distinctBy<T, U extends string | number>(array: T[], mapFn: (el: T) => U) {
  const uniqueKeys = new Set(array.map(mapFn));
  return array.filter((el) => uniqueKeys.has(mapFn(el)));
}

U물론 해시 가능한 어디 입니까? 객체의 경우 https://www.npmjs.com/package/es6-json-stable-stringify 가 필요할 수 있습니다.

참고 URL : https://stackoverflow.com/questions/15125920/how-to-get-distinct-values-from-an-array-of-objects-in-javascript

반응형