Ramda: Memfilter melalui array dengan nilai terkait
Ini adalah kumpulan data awal saya:
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]
}];
Dengan menyaring type: n => n > 0dan meneruskan hasil melalui arr1, saya ingin menghasilkan arr2dengan Ramda. Jika nnilai th dikeluarkan sebagai hasil dari filter, maka nnilai th di array lain juga dikecualikan.
arr2 = [{
url: ['https://example.com/B.jpg?', 'https://example.com/C.jpg?'],
width: ['w=400', 'w=500'],
type: [1, 2]
}];
Saya mencoba kode di bawah ini, tetapi tidak berhasil ...
const isgt0 = n => n > 0 ;
const arr2 = R.applySpec({
url : arr1,
width : arr1,
type : R.filter(isgt0),
});
console.log(arr2(arr1));
Setelah saya mendapatkan objek yang diinginkan, saya bermaksud ke R.transposearray untuk menghasilkan URL seperti:[https://example.com/B.jpg?w=400, https://example.com/C.jpg?w=500]
Jawaban
Langkah utamanya adalah:
Dapatkan array nilai dengan 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?']
Ubah urutannya menjadi array item dengan indeks yang sama:
[-1, 'w=300', 'https://example.com/A.jpg?'][1, 'w=400', 'https://example.com/B.jpg?'][1, 'w=500', 'https://example.com/C.jpg?']
Filter menurut indeks 0(asli type), ubah urutannya kembali, lalu rekonstruksi objek menggunakan 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>
Cara lain untuk memikirkannya secara lebih umum adalah dengan memfilter menggunakan objek konfigurasi yang menampung pengujian untuk diterapkan pada berbagai properti. Ini dia saja type, tapi cukup mudah untuk membayangkan orang lain.
Solusi saya untuk masalah ini dikonfigurasi dengan objek ini:
{
type: n => n > 0
}
Solusi ini menggunakan banyak fungsi Ramda, tetapi juga menggunakan Array.prototype.filteruntuk memiliki akses ke parameter indeks filter. Kita dapat memilih R.addIndexsebagai gantinya, tetapi saya hanya akan repot jika saya mencoba membuatnya bebas poin, yang tampaknya tidak berharga di sini. Akan terlihat seperti ini:
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>
Dengan objruang lingkup, kami membuat test, yang agak setara dengan
allPass([
i => obj['type'][i] > 0
])
Jika kita memiliki lebih banyak kondisi di objek konfigurasi asli, mereka juga akan ada di daftar ini.
Kemudian kami memfilter indeks, untuk melihat mana yang lolos tes ini.
Akhirnya kami memetakan objek kami, memfilter setiap larik untuk menyimpan hanya yang indeksnya ada dalam daftar.
Meskipun ini seharusnya berfungsi, dan cukup umum, ini menunjuk pada masalah dengan struktur data Anda. Saya menyarankan bahwa sebanyak mungkin, Anda menghindar dari situasi di mana struktur bergantung pada indeks bersama. Bagi saya, satu-satunya penggunaan yang masuk akal adalah untuk format serialisasi yang relatif kompak. Saat deserialisasi, saya akan segera mengembalikannya ke sesuatu yang lebih berguna, mungkin sesuatu seperti
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}
]
Struktur ini jauh lebih mudah untuk dikerjakan. Misalnya, data.filter(({type}) => type > 0)akan sama dengan pekerjaan di atas, jika Anda memulai dengan struktur ini.
Ini mungkin sedikit membantu
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>