Jedna linijka, aby pobrać niektóre właściwości z obiektu w ES 6

153

Jak można napisać funkcję, która ma tylko kilka atrybutów w najbardziej zwarty sposób w ES6?

Wymyśliłem rozwiązanie z użyciem destrukturyzacji + uproszczony literał obiektu, ale nie podoba mi się, że lista pól jest powtarzana w kodzie.

Czy istnieje jeszcze smuklejsze rozwiązanie?

(v) => {
    let { id, title } = v;
    return { id, title };
}
kirilloid
źródło

Odpowiedzi:

124

Tutaj jest coś szczuplejszego, chociaż nie unika to powtarzania listy pól. Używa „destrukturyzacji parametrów”, aby uniknąć konieczności stosowania vparametru.

({id, title}) => ({id, title})

(Zobacz działający przykład w tej innej odpowiedzi ).

Rozwiązanie @ EthanBrown jest bardziej ogólne. Oto bardziej idiomatyczna wersja tego, która wykorzystuje Object.assigni obliczone właściwości ( [p]część):

function pick(o, ...props) {
    return Object.assign({}, ...props.map(prop => ({[prop]: o[prop]})));
}

Jeśli chcemy zachować atrybuty właściwości, takie jak configurablei pobierające i ustawiające, jednocześnie pomijając niewliczalne właściwości, to:

function pick(o, ...props) {
    var has = p => o.propertyIsEnumerable(p),
        get = p => Object.getOwnPropertyDescriptor(o, p);

    return Object.defineProperties({},
        Object.assign({}, ...props
            .filter(prop => has(prop))
            .map(prop => ({prop: get(props)})))
    );
}
Dan Dascalescu
źródło
10
+1 ładna odpowiedź, torazaburo; dzięki za uświadomienie mi Object.assign; es6 jest jak choinka z tak wieloma prezentami pod nią, że wciąż znajduję prezenty miesiące po wakacjach
Ethan Brown
Wystąpił błąd: opis właściwości musi być obiektem: niezdefiniowany. Nie powinno być filter(...).map(prop => ({[prop]: get(prop)})))?
Endless
W przypadku pierwszego pick()wdrożenia możesz również zrobić coś takiegoreturn props.reduce((r, prop) => (r[prop] = o[prop], r), {})
Patrick Roberts
niestety ta wersja pick nie będzie bezpieczna w przepływie ani w maszynie. jeśli chcesz mieć bezpieczeństwo typów, nie ma sposobu na obejście przypisania zniszczenia oryginalnego obiektu, a następnie przypisanie każdego do nowego obiektu.
duhseekoh
Kiedy właściwość nie istnieje w obiekcie, otrzymasz undefined. Czasami to ma znaczenie. Poza tym niezły pomysł.
x-yuri
43

Nie sądzę, że istnieje jakiś sposób, aby uczynić go znacznie bardziej zwarty niż odpowiedź (lub torazburo'S), ale przede wszystkim to, co próbujesz zrobić, to Emulate podkreślenia jest pickoperacja . Byłoby łatwo ponownie zaimplementować to w ES6:

function pick(o, ...fields) {
    return fields.reduce((a, x) => {
        if(o.hasOwnProperty(x)) a[x] = o[x];
        return a;
    }, {});
}

W takim razie masz przydatną funkcję wielokrotnego użytku:

var stuff = { name: 'Thing', color: 'blue', age: 17 };
var picked = pick(stuff, 'name', 'age');
Ethan Brown
źródło
Dzięki. To nie jest odpowiedź na moje pytanie, ale bardzo fajny dodatek.
kirilloid
7
(wzruszając ramionami) Czuję, że jest to odpowiedź na twoje rozwiązanie; nie ma szczuplejszego rozwiązania ogólnego (rozwiązanie torazaburo usuwa dodatkowe informacje, ale podstawowy problem - wszystkie nazwy właściwości muszą być zapisane dwukrotnie - oznacza, że ​​nie skaluje się lepiej niż twoje rozwiązanie). Moje rozwiązanie przynajmniej dobrze się skaluje ... pickraz popraw funkcję, a możesz wybrać dowolną liczbę właściwości, a to ich nie podwoi.
Ethan Brown
1
Dlaczego używasz hasOwnProperty? Jeśli pola są wybierane ręcznie, nawet inwydaje się bardziej odpowiednie; chociaż chciałbym całkowicie pominąć sprawdzanie i po prostu pozwolić im domyślnie undefined.
Bergi
Bergi, to rozsądna uwaga ... Po prostu uważam, że właściwości (nie metody) w łańcuchu prototypów są dziwne i „śmierdzące” (ponieważ są to zapach kodu) i wolę je domyślnie odfiltrowywać. Jeśli istnieje aplikacja, która wymaga właściwości prototypu, cóż ... może być na to opcja.
Ethan Brown
co z tablicami json!
Rizwan Patel
19

Sztuczka pozwalająca rozwiązać ten problem jako jednolinijkowy polega na odwróceniu przyjętego podejścia: zamiast zaczynać od oryginalnego obiektu orig, można zacząć od kluczy, które chcą wyodrębnić.

Używając Array#reducego, można następnie przechowywać każdy potrzebny klucz w pustym obiekcie, który jest przekazywany jako funkcja initialValuedla tej funkcji.

Tak jak to:

const orig = {
  id: 123456789,
  name: 'test',
  description: '…',
  url: 'https://…',
};

const filtered = ['id', 'name'].reduce((result, key) => { result[key] = orig[key]; return result; }, {});

console.log(filtered); // Object {id: 123456789, name: "test"}

Bramus
źródło
11

Trochę krótsze rozwiązanie wykorzystujące operator przecinka:

const pick = (O, ...K) => K.reduce((o, k) => (o[k]=O[k], o), {})

console.log(
  pick({ name: 'John', age: 29, height: 198 }, 'name', 'age')
)  

shesek
źródło
jak tego używać? Czy możesz podać przykład?
Tomas M
1
Działa podobnie jak inne pickfunkcje w tym wątku:pick({ name: 'John', age: 29, height: 198 }, 'name', 'age')
shesek
8

Propozycja właściwości spoczynku / rozłożenia obiektu TC39 sprawi, że będzie to całkiem sprytne:

let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
z; // { a: 3, b: 4 }

(Ma tę wadę, że tworzy zmienne xi y, których możesz nie potrzebować).

alxndr
źródło
33
To wygodna forma omit, ale niepick
kirilloid
5
Bardzo chciałbym zobaczyć wariant, który robi dokładne przeciwieństwo tego jako propozycja ES:let { a, b } as z = { x: 1, y: 2, a: 3, b: 4 }
gfullam
3

Możesz użyć destrukturyzacji obiektów do wypakowania właściwości z istniejącego obiektu i przypisania ich do zmiennych o różnych nazwach - pól nowego, początkowo pustego obiektu.

const person = {
  fname: 'tom',
  lname: 'jerry',
  aage: 100,
}

let newPerson = {};

({fname: newPerson.fname, lname: newPerson.lname} = person);

console.log(newPerson);

Saksham
źródło
(indeks): 36 Uncaught SyntaxError: Nieprawidłowy cel przydziału destrukturyzacji
Remzes
@Remzes nie wie, gdzie i jak to wykonujesz, ale działa to dobrze w edytorze kodu SO i narzędziach programistycznych Chrome.
Saksham
Użyłem jsfiddle
Remzes
Poprawiłem nieco twoją odpowiedź, ale wciąż jest zbyt szczegółowa, w porównaniu z tym, o co prosił OP. Powtarza nie tylko nazwy pól, ale także nazwę nowego obiektu.
Dan Dascalescu
3

ES6 była najnowszą specyfikacją w momencie pisania pytania. Jak wyjaśniono w tej odpowiedzi , wybieranie kluczy jest znacznie krótsze w ES2019 niż w ES6:

Object.fromEntries(
  Object.entries(obj)
  .filter(([key]) => ['foo', 'bar'].includes(key))
)
Estus Flask
źródło
2

Obecnie istnieje propozycja strawmana dotycząca ulepszenia skróconej składni obiektów JavaScript, która umożliwiłaby „wybieranie” nazwanych właściwości bez ich powtarzania:

const source = {id: "68646", genre: "crime", title: "Scarface"};
const target = {};
Object.assign(target, {source.title, source.id});

console.log(picked);
// {id: "68646", title: "Scarface"}

Niestety, ta propozycja nie wydaje się wkrótce nigdzie trafiać. Ostatnio edytowany w lipcu 2017 r. I nadal jest szkicem na etapie 0 , co sugeruje, że autor mógł porzucić go lub o nim zapomnieć.

ES5 i starsze (tryb nieścisły)

Najbardziej zwięzły możliwy skrót, jaki przychodzi mi do głowy, dotyczy cechy starożytnego języka, której nikt już nie używa:

Object.assign(target, {...(o => {
    with(o) return { id, title };
})(source)});

withinstrukcje są zabronione w trybie ścisłym, przez co takie podejście jest bezużyteczne dla 99,999% współczesnego JavaScript. Szkoda, bo to jedyne w połowie przyzwoite zastosowanie tej withfunkcji, jakie znalazłem . 😀


źródło
1

Mam rozwiązanie podobne do rozwiązania Ethana Browna, ale jeszcze krótsze - pickfunkcję. Inna funkcja pick2jest nieco dłuższa (i wolniejsza), ale pozwala zmieniać nazwy właściwości w podobny sposób jak w ES6.

const pick = (o, ...props) => props.reduce((r, p) => p in o ? {...r, [p]: o[p]} : r, {})

const pick2 = (o, ...props) => props.reduce((r, expr) => {
  const [p, np] = expr.split(":").map( e => e.trim() )
  return p in o ? {...r, [np || p]: o[p]} : r
}, {}) 

Oto przykład użycia:

const d = { a: "1", c: "2" }

console.log(pick(d, "a", "b", "c"))        // -> { a: "1", c: "2" }
console.log(pick2(d, "a: x", "b: y", "c")) // -> { x: "1", c: "2" }
Alexandr Priezzhev
źródło
1
Jaki jest powód negatywnego głosu? Czy to nie działa dla Ciebie?
Alexandr Priezzhev
0

Potrzebowałem tego rozwiązania, ale nie wiedziałem, czy proponowane klucze są dostępne. Więc wziąłem odpowiedź @torazaburo i poprawiłem dla mojego przypadku użycia:

function pick(o, ...props) {
  return Object.assign({}, ...props.map(prop => {
    if (o[prop]) return {[prop]: o[prop]};
  }));
}

// Example:
var person = { name: 'John', age: 29 };
var myObj = pick(person, 'name', 'sex'); // { name: 'John' }
Alwin Kesler
źródło
0

zainspirowany podejściem redukującym https://stackoverflow.com/users/865693/shesek :

const pick = (orig, ...keys) => keys.reduce((acc, key) => ({...acc, [key]: orig[key]}), {})

stosowanie:

pick({ model : 'F40', manufacturer: 'Ferrari', productionYear: 1987 }, 'model', 'productionYear') prowadzi do: {model: "F40", productionYear: 1987}

Kevin K.
źródło