Ramda: Memfilter melalui array dengan nilai terkait

Sep 02 2020

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

3 OriDrori Sep 02 2020 at 20:55

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>

1 ScottSauyet Sep 03 2020 at 21:17

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.

Hitmands Sep 02 2020 at 15:00

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>