Ramda: filtraggio attraverso array con valore associato
Questo è il mio set di dati iniziale:
arr1 = [{
url: ['https://example.com/A.jpg?', 'https://example.com/B.jpg?', 'https://example.com/C.jpg?'],
width: ['w=300', 'w=400', 'w=500'],
type: [-1, 1, 2]
}];
Filtrando con type: n => n > 0e passando il risultato attraverso il arr1, vorrei produrre arr2con Ramda. Se nil valore esimo viene escluso come risultato del filtro, viene escluso anche il nvalore esimo in un altro array.
arr2 = [{
url: ['https://example.com/B.jpg?', 'https://example.com/C.jpg?'],
width: ['w=400', 'w=500'],
type: [1, 2]
}];
Ho provato il codice qui sotto, ma non funziona ...
const isgt0 = n => n > 0 ;
const arr2 = R.applySpec({
url : arr1,
width : arr1,
type : R.filter(isgt0),
});
console.log(arr2(arr1));
Una volta ottenuto l'oggetto desiderato, intendo che R.transposel'array generi un URL come:[https://example.com/B.jpg?w=400, https://example.com/C.jpg?w=500]
Risposte
I passaggi principali sono:
Ottieni gli array dei valori con R.props:
[-1, 1, 2]['w=300', 'w=400', 'w=500']['https://example.com/A.jpg?', 'https://example.com/B.jpg?', 'https://example.com/C.jpg?']
Trasponili a matrici di elementi con lo stesso indice:
[-1, 'w=300', 'https://example.com/A.jpg?'][1, 'w=400', 'https://example.com/B.jpg?'][1, 'w=500', 'https://example.com/C.jpg?']
Filtra per indice 0(l'originale type), trasponi indietro e poi ricostruisci l'oggetto usando R.applySpec.
const { pipe, props, transpose, filter, propSatisfies, gt, __, tranpose, applySpec, nth, map } = R
const filterProps = pipe(
props(['type', 'width', 'url']), // get an array of property
transpose, // convert to arrays of all property values with the same index
filter(propSatisfies(gt(__, 0), 0)), // filter by the type (index 0)
transpose, // convert back to arrays of each type
applySpec({ // reconstruct the object
type: nth(0),
width: nth(1),
url: nth(2),
})
)
const data = [
{
type: [-1, 1, 2],
width: ['w=300', 'w=400', 'w=500'],
url: [
'https://example.com/A.jpg?',
'https://example.com/B.jpg?',
'https://example.com/C.jpg?',
],
}
]
const result = map(filterProps, data)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.js" integrity="sha512-3sdB9mAxNh2MIo6YkY05uY1qjkywAlDfCf5u1cSotv6k9CZUSyHVf4BJSpTYgla+YHLaHG8LUpqV7MHctlYzlw==" crossorigin="anonymous"></script>
Un altro modo per pensarci in modo più generico è filtrare utilizzando un oggetto di configurazione che contiene i test da applicare per varie proprietà. Qui è solo type, ma è abbastanza facile immaginarne altri.
La mia soluzione per questo problema è configurata con questo oggetto:
{
type: n => n > 0
}
Questa soluzione utilizza molte funzioni Ramda, ma utilizza anche Array.prototype.filterper avere accesso al parametro index di filter. Potremmo scegliere R.addIndexinvece, ma mi preoccuperei solo se cercassi di renderlo privo di punti, il che non sembra utile qui. Ecco come potrebbe apparire:
const filterOnProps = (config) => (obj) => {
const test = allPass (map(([k, v]) => (i) => v (obj [k] [i]), toPairs (config)))
const indices = filter (test) (range (0, values (obj) [0] .length))
return map(a => a .filter ((_, i) => contains (i, indices)), obj)
}
const transform = map (filterOnProps ({type: n => n > 0}))
const arr1 = [{url: ['https://example.com/A.jpg?', 'https://example.com/B.jpg?', 'https://example.com/C.jpg?'], width: ['w=300', 'w=400', 'w=500'], type: [-1, 1, 2]}]
console .log (transform (arr1))
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>
<script> const {allPass, map, toPairs, filter, range, values, contains} = R </script>
Con l' objambito, creiamo test, che sarà in qualche modo equivalente a
allPass([
i => obj['type'][i] > 0
])
Se avessimo più condizioni nell'oggetto di configurazione originale, sarebbero anche in questo elenco.
Quindi filtriamo gli indici, per vedere su quali il record supera questo test.
Infine mappiamo il nostro oggetto, filtrando ogni array per mantenere solo quelli in cui l'indice è nell'elenco.
Anche se dovrebbe funzionare ed è ragionevolmente generico, indica un problema con la struttura dei dati. Suggerirei che, per quanto possibile, eviti le situazioni in cui le strutture dipendono da indici condivisi. A mio avviso l'unico uso ragionevole di questo è per un formato di serializzazione relativamente compatto. Alla deserializzazione, lo reidraterei immediatamente in qualcosa di più utile, forse qualcosa di simile
const data = [
{url: 'https://example.com/A.jpg?', width: 'w=300', type: -1},
{url: 'https://example.com/B.jpg?', width: 'w=400', type: 1},
{url: 'https://example.com/C.jpg?', width: 'w=500', type: 2}
]
Questa struttura è molto più facile da lavorare. Ad esempio, data.filter(({type}) => type > 0)sarebbe l'equivalente del lavoro sopra, se partissi con questa struttura.
Questo potrebbe aiutare un po '
const gte1 = R.filter(R.gte(R.__, 1));
const fn = R.map(
R.evolve({
type: gte1,
}),
);
// =====
const data = [
{
type: [-1, 1, 2],
width: ['w=300', 'w=400', 'w=500'],
url: [
'https://example.com/A.jpg?',
'https://example.com/B.jpg?',
'https://example.com/C.jpg?',
],
}
];
console.log(
fn(data),
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js" integrity="sha512-rZHvUXcc1zWKsxm7rJ8lVQuIr1oOmm7cShlvpV0gWf0RvbcJN6x96al/Rp2L2BI4a4ZkT2/YfVe/8YvB2UHzQw==" crossorigin="anonymous"></script>