0

Is there any way to count items that occured in a row (in series without interruption)? Lets say we have

<span><div class="amount large">100</div></span>
<span><div class="amount large">120</div></span>
<span><div class="amount large">300</div></span>
<span><div class="amount small">90</div></span>
<span><div class="amount large">110</div></span>
<span><div class="amount large">520</div></span>
... List continues

and I did separate them into

function isBigEnough(element, index, array) {
  return element >= 100;
}

function isNotBigEnough(element, index, array) {
  return element <= 99;
}

var values = [];

$(".amount").slice(0, 5).each(function(index){
    values.push($(this).text());
});

if (values.every(isBigEnough)) {
    console.log("Large");
} else if (values.every(isNotBigEnough)) {
    console.log("Small");
} else {console.log("No Series")}

How can I count how many elements occured in a row, which in my case would be 3.

I tried

 $("span .large ~ span .large").length 

But I know there is sth wrong (or not right at all).

Klajdi
  • 337
  • 1
  • 10
  • So you want to count the number of elements with `small` and `large` classes? – aphextwix Mar 30 '16 at 18:29
  • Maybe `values.push(parseInt($(this).text(), 10))`... – Andy Mar 30 '16 at 18:30
  • I need the one that occured lately, in this case is "large" elements. And it (total elements in series) would be 3. – Klajdi Mar 30 '16 at 18:33
  • `"span .large ~ span .large"` selects `.large` elements that are descendants of general siblings of `span .large span` ; those elements do not appear to exist at `html` ? – guest271314 Mar 30 '16 at 18:35
  • In series meaning that there is no interruption. It is true only if "large" "large" "large" and not "large" "large" "small" "large" – Klajdi Mar 30 '16 at 18:36
  • I wanted to select all span .large elements preceeded by span .large elemenst. This would mean that there is more than 1 element in row. – Klajdi Mar 30 '16 at 18:40
  • _"I wanted to select all span .large elements preceeded by span .large elemenst. This would mean that there is more than 1 element in row."_ Given `html` at Question, result should be `"No Series"` , as there are not five `.large` elements that are adjacent siblings. Selector `$(".amount").slice(0, 5)` selects five `.amount` elements, though does not filter if each element is an adjacent sibling of a `.large` element – guest271314 Mar 30 '16 at 18:43
  • Well, I do want to know their size in last 5 occurences, but in this question I want to know the amount of elements that occured lately in series, which I want to tell by class. – Klajdi Mar 30 '16 at 18:50
  • Not certain interpret requirement correctly. Should less than five `.large` elements that are adjacent siblings return `false` ? – guest271314 Mar 30 '16 at 18:53
  • It should return the number of adjacent siblings. – Klajdi Mar 30 '16 at 18:54

2 Answers2

0

No, thats where you seem to be mistaken :) , the requirement is to find the number of elements in series (adjacent siblings),

At html at Question, there are not any .large elements which are adjacent siblings of .large elements; as all .large elements are descendants of span elements. See Adjacent sibling selectors .

You can use :has() selector span:has(.large) + span:has(.large) to return elements that are adjacent siblings of span that contains .large element. Note, this should return the span:has(.large) of the first span:has(.large) not including the first span:has(.large). Though the accurate .length can be derived by determining the total .length of span:has(.large) elements then, either adding one or multiplication and division.

var adjacentLarge = $("span:has(.large) + span:has(.large)");

console.log(
  adjacentLarge.length, // this should return 3; .large having text 120, 300, 520
  $("span:has(.large)").length 
  * (adjacentLarge.length / adjacentLarge.length) // 5
); 
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<span><div class="amount large">100</div></span>
<span><div class="amount large">120</div></span>
<span><div class="amount large">300</div></span>
<span><div class="amount small">90</div></span>
<span><div class="amount large">110</div></span>
<span><div class="amount large">520</div></span>

You can use .filter(), .prev(), .is() , .nextUntil(), .nextAll(), .add() , .next()

var adjacentLarge = $("span:has(.large):first").filter(function() {
  return !$(this).prev().is(":has(.large)")
});

var firstLargeSet = adjacentLarge.nextUntil(":not(:has(.large))")
                    .add("span:has(.large):first");

var secondLargeSet = firstLargeSet.filter(":eq(-1)")
                     .nextAll(":has(.large)")
                     .filter(function() {
                       return $(this).next().is(":has(.large)") 
                              || $(this).prev().is(":has(.large)")
                     });

function isBigEnough(element, index, array) {
  return element >= 100;
}

function isNotBigEnough(element, index, array) {
  return element <= 99;
}

var values1 = [];

firstLargeSet.each(function(index) {
  console.log(this.textContent); // 100, 120, 300
  values1.push($(this).text());
});

if (values1.every(isBigEnough)) {
  console.log("Large"); // Large
} else if (values1.every(isNotBigEnough)) {
  console.log("Small");
} else {
  console.log("No Series")
}

var values2 = [];

secondLargeSet.each(function(index) {
  console.log(this.textContent); // 110, 520
  values2.push($(this).text());
});

if (values2.every(isBigEnough)) {
  console.log("Large"); // Large
} else if (values2.every(isNotBigEnough)) {
  console.log("Small");
} else {
  console.log("No Series")
}

var adjacentLarge = $("span:has(.large):first").filter(function() {
  return !$(this).prev().is(":has(.large)")
});

var firstLargeSet = adjacentLarge.nextUntil(":not(:has(.large))")
                    .add("span:has(.large):first");

var secondLargeSet = firstLargeSet.filter(":eq(-1)")
                     .nextAll(":has(.large)")
                     .filter(function() {
                       return $(this).next().is(":has(.large)") 
                              || $(this).prev().is(":has(.large)")
                     });

function isBigEnough(element, index, array) {
  return element >= 100;
}

function isNotBigEnough(element, index, array) {
  return element <= 99;
}

var values1 = [];

firstLargeSet.each(function(index) {
  console.log(this.textContent); // 100, 120, 300
  values1.push($(this).text());
});

if (values1.every(isBigEnough)) {
  console.log("Large"); // Large
} else if (values1.every(isNotBigEnough)) {
  console.log("Small");
} else {
  console.log("No Series")
}

var values2 = [];

secondLargeSet.each(function(index) {
  console.log(this.textContent); // 110, 520
  values2.push($(this).text());
});

if (values2.every(isBigEnough)) {
  console.log("Large"); // Large
} else if (values2.every(isNotBigEnough)) {
  console.log("Small");
} else {
  console.log("No Series")
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<span><div class="amount large">100</div></span>
<span><div class="amount large">120</div></span>
<span><div class="amount large">300</div></span>
<span><div class="amount small">90</div></span>
<span><div class="amount large">110</div></span>
<span><div class="amount large">520</div></span>
guest271314
  • 1
  • 15
  • 104
  • 177
  • No, that code works like this: If in 5 occurences, the 5th are big, or small, it would tell if they are small or big, and if they are mixed, it would tell that the series are not long enough. Now I want to count them, not separate the series into big or small. – Klajdi Mar 30 '16 at 18:47
  • Is requirement to 1) check if five `.large` elements are adjacent siblings in `html` ? Then if 1) is true, 2) check if the text of each of those elements is within a given range ? – guest271314 Mar 30 '16 at 18:48
  • No, thats where you seem to be mistaken :) , the requirement is to find the number of elements in series (adjacent siblings), – Klajdi Mar 30 '16 at 18:52
  • What do you mean by "won't work" ? Note, there are not any `.large` elements which are adjacent siblings of `.large` elements at `html` at Question. – guest271314 Mar 30 '16 at 19:17
  • The second code snippet worked, I would really appreciate if you told how that works. And, Is there any way to find which one is the first between `firstLargeSet` and `firstSmallSet` ? – Klajdi Mar 31 '16 at 12:46
  • This would return first largests `var adjacentLarge = $("span:has(.large):first").filter(function() { return !$(this).prev().is(":has(.large)") }); var firstLargeSet = adjacentLarge.nextUntil(":not(:has(.large))") .add("span:has(.large):first"); var secondLargeSet = firstLargeSet.filter(":eq(-1)") .nextAll(":has(.large)") .filter(function() { return $(this).next().is(":has(.large)") || $(this).prev().is(":has(.large)") });` and secondLargest worked for me – Klajdi Mar 31 '16 at 12:48
  • @Klajdi Find first element that contain `.large` as descendant, find next elements that contain `.large` up until element that does not contain `.large`. – guest271314 Mar 31 '16 at 14:08
0

Walks through the elements and compares whether two in a row are equal; if they are, increments a counter. If not, creates a new counter. Last step takes the max of all counters.

Note that this knows nothing about the structure and does not enforce any. It will get all elements with a class of amount. So, you can modify the selector on the first line if you want to narrow down the results to a more specific part of the DOM.

$.makeArray($(".amount"))
  .reduce(function(pVal,cVal,i,array) {
      if (i > 0 && ((array[i-1].classList.contains("large") && array[i].classList.contains("large"))
          || (array[i-1].classList.contains("small") && array[i].classList.contains("small"))))
        pVal[pVal.length-1]++;
      else 
        pVal.push(1);
      return pVal;
   }, [] )
  .reduce( (a,b) => a > b ? a : b , 0 )
Josh
  • 4,726
  • 2
  • 20
  • 32
  • Uncaught TypeError: Reduce of empty array with no initial value – Klajdi Mar 31 '16 at 12:32
  • @Klajdi - thanks, modified last line to add an initial value of 0 so no errors on an empty element array -- does this answer your original question? I created your DOM structure and it worked correctly when tested. – Josh Mar 31 '16 at 20:07