Получаем результирующую длину Array.prototype.some()

#javascript #arrays

#javascript #массивы

Вопрос:

У меня есть условие, которое проверяется через Array.prototype.some() . Учитывая этот массив:

 const coolArray = [
  { isCool: false },
  { isCool: false },
  { isCool: true }
]

const isCool = coolArray.some(item => item.isCool === true)

if (isCool) {
  console.log("hello I'm cool!")
}
  

Однако я хотел, чтобы проверка выполнялась, когда item.isCool есть true и по крайней мере два элемента в массиве соответствуют условию. Таким образом, приведенный выше пример не выводил бы сообщение, поскольку существует только одно true условие.

В MDN ссылка на этот метод является arr.some(callback(element[, index[, array]])[, thisArg]) . Однако [, array] ссылаемся на исходный массив вместо их клона, поэтому выполнение приведенного ниже результата приводит к тому же результату:

 const isCool = coolArray.some((item, index, arr) => {
  return item.isCool === true amp;amp; arr.length > 1
})
  

Я знаю, что мог бы избежать .some() и выполнить итерацию по массиву с помощью map или for и сохранить результаты во внешнем массиве, чтобы я мог проверить длину, что-то похожее на:

 const isCoolArr = []
coolArray.map(item => item.isCool ? isCoolArr.push(item) : false)
console.log('expected result:', isCoolArr.length) // outputs 1
  

Но я не особенно доволен этим методом и ищу более простые альтернативы. Может .some() подойти то, что мне нужно, или мне нужна другая альтернатива? Существуют ли какие-либо, кроме случая, о котором я упомянул выше?

Комментарии:

1. coolArray.filter(item => item.isCool).length >= 2

2. @p.s.w.g atleast two так что это должно >= 2 или > 1

Ответ №1:

Array.reduce

 const coolArray = [
  { isCool: false },
  { isCool: false },
  { isCool: true }
]

const count = coolArray.reduce((accum, item) => Number(item.isCool)   accum, 0)

if (count >= 2) {
  console.log("Hello I'm cool!")
}
  

Комментарии:

1. Использование post-increment не сработало бы, так оно и есть 🙂

2. coolArray.reduce((накопление, элемент) => Число (item.IsCool) накопление, 0) Явное лучше, чем неявное.

Ответ №2:

С .filter() вы теряете преимущество короткого замыкания, которое получаете с .some() . Одним из вариантов было бы использовать переменную, внешнюю по отношению к обратному вызову.

 const coolArray = [{isCool: false},{isCool: false},{isCool: true}]
let count = 0

const isCool = coolArray.some(item => (count  = item.isCool) >= 2)

if (isCool) {
  console.log("hello I'm cool!")
} else {
  console.log("not cool enough")
}  

Это использует преимущества преобразования логических значений в числа. Если вам это не нравится, вы можете быть более откровенными.

 const coolArray = [{isCool: false},{isCool: false},{isCool: true}]
let count = 0

const isCool = coolArray.some(item => item.isCool amp;amp;   count >= 2)

if (isCool) {
  console.log("hello I'm cool!")
} else {
  console.log("not cool enough")
}  

Или сделайте это без добавления дополнительной переменной!

 const coolArray = [{isCool: false},{isCool: false},{isCool: true}]
let isCool = 0;

isCool = coolArray.some(item => item.isCool amp;amp;   isCool >= 2);

if (isCool) {
  console.log("hello I'm cool!")
} else {
  console.log("not cool enough")
}  

Ответ №3:

Вы можете использовать фильтр, а затем проверить длину

 const coolArray = [
  { isCool: false },
  { isCool: false },
  { isCool: true }
]

const isCool = coolArray.filter(item => item.isCool === true)

if (isCool.length > 1 ) {
  console.log("hello I'm cool!")
} else{
  console.log('length is less than 2')
}  

Ответ №4:

Другие люди предложили использовать filter и проверить length . Я хотел бы создать по крайней мере метод, который сломался бы, если условие выполнено

 const coolArray = [
  { isCool: false },
  { isCool: false },
  { isCool: true }
]

function x(cb,num){
  let count = 0;
  for(let i =0;i<this.length;i  ){
    if(cb(this[i],i,this)) count  ;
    if(count === num) return true;
  }
  return false;
}

Object.defineProperty(Array.prototype,'atLeast',{
  value:x,
  writeable:true
})

console.log(coolArray.atLeast(x => x.isCool === true,1)); //true
console.log(coolArray.atLeast(x => x.isCool === true,2)); //false  

Комментарии:

1. Я бы всегда использовал defineProperty при изменении прототипа, чтобы избежать побочных эффектов.

2. @JonasWilms Я отредактировал код. Я хотел бы знать эти побочные эффекты.

3. for(let key in []) console.log(key);

Ответ №5:

Вот простая универсальная реализация, которая останавливается при достижении предела:

 let someTimes = (array, fn, count) => array.some(x => !(count -= Boolean(fn(x))));

console.log(someTimes(
    [1,2,3,44,5,66,7,99],
    x => x > 10,
    2
))  

или, возможно, даже лучше,

 let countDown = (fn, count) => x => !(count -= !!fn(x));


console.log(
    [1, 2, 3, 44, 5, 66, 7, 99].some(countDown(x => x > 10, 2))
)  

В большинстве случаев оптимизация раннего завершения не стоит таких хлопот, и вы можете просто использовать гораздо более полезную общую функцию счетчика:

 let countWhere = (array, fn) => array.reduce((n, x) => n   Boolean(fn(x)), 0);

console.log(countWhere(
    [1,2,3,44,5,66,7,99],
    x => x > 10
) >= 2)