3
\$\begingroup\$

I wrote this algorithm which takes input data from something like a CSV file or a large array that contain a series of String elements in the format of "count, FQDN" and then adds or increments the count of each domain component up to the complete FQDN. For example:

// Sample output (in any order/format):
// getTotalsByDomain(counts)
//   1320    com
//   900    google.com
//   410    yahoo.com
//    60    mail.yahoo.com
//    10    mobile.sports.yahoo.com
//    50    sports.yahoo.com
//    10    stackoverflow.com
//     3  org
//     3  wikipedia.org
//     2  en.wikipedia.org
//     1  es.wikipedia.org
//     1  mobile.sports
//   1  sports

let counts = [ "900,google.com",
    "60,mail.yahoo.com", 
    "10,mobile.sports.yahoo.com", 
    "40,sports.yahoo.com", 
    "300,yahoo.com", 
    "10,stackoverflow.com", 
    "2,en.wikipedia.org", 
    "1,es.wikipedia.org", 
    "1,mobile.sports" ];

I was able to do this pretty well with the below algorithm, but I am concerned with the inner for loop which uses var 'j'. I felt like the only way that I could incrementally parse the domain components from the already split array was to create another array which unshifted the domain components to partially create a new array until I completed all of the components of the given FQDN element.

function getDomainHits(arr){

  var splitCount = [];
  var domainCountDict = {};

  for (var i = 0; i < arr.length; i++){

    splitCount = arr[i].split(",");

    var curCnt = 0;
          if (splitCount[0]){

            curCnt = splitCount[0];

          }

    var domain = [];
    var currentDom = [];
          if (splitCount[1] != undefined && splitCount[1]){

            domain = splitCount[1].split(".");


            for (var j = domain.length - 1; j >= 0; j--){


               currentDom.unshift(domain.pop());

               /*console.log("current iter: " + k + "\n"
                           + "currentDom: " + currentDom.join(".") + "\n"
                           + "current count: " + curCnt + "\n");*/

              if (currentDom.join(".") in domainCountDict){
                /*console.log("currentDom2: " + currentDom.join("."));
                console.log("increment existing");*/
                domainCountDict[currentDom.join(".")] += parseInt(curCnt);  


              }
              if (!(currentDom.join(".") in domainCountDict)){
                /*console.log("currentDom3: " + currentDom.join("."));
                console.log("increment new");*/
                domainCountDict[currentDom.join(".")] = parseInt(curCnt);


                //console.log(domainCountDict);
              }

            }

          }

  }

  return domainCountDict;

}

console.log(getDomainHits(counts));

If you want to see a complete walkthrough of my logic then you can see my answer to my question in Stack Overflow

\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

You can modify the input until there's nothing left of it. In this example, I've used a regex with .replace() to shorten the FQDN.

If you don't like regex, you could instead split the domain name on ., then in the loop use dom.join(".") to compose the object key, followed by dom.shift() to shorten the array. With the array technique, the loop conditional is dom.length (instead of just dom).

function getDomainHits(arr){
    let total={};
    arr.forEach( row => {
        let [hits, dom] = row.split(",");
        hits=parseInt(hits);
        while ( hits && dom ) { 
            total[dom] = (total[dom] || 0) + hits;
            dom = dom.replace( /^[^.]*\.?/, '' )
        }
    });
    return total;
}
\$\endgroup\$
5
  • \$\begingroup\$ Wow this is a very elegant solution. Thanks for that I really do need to learn my regex patterns. I used to be better with them, but I never kept up with it. Do you recommend a resource that I could use to catch back up? \$\endgroup\$
    – brndng
    Mar 14, 2019 at 3:33
  • 1
    \$\begingroup\$ Start small and spend time dissecting patterns to see what they are doing. There are tools like regexbuddy to help with analysis; some people find those helpful, some don't. It's a handy skill, but in many ways nonessential—the array-split version of this code is only one line longer and uses no regex at all. \$\endgroup\$ Mar 14, 2019 at 4:44
  • \$\begingroup\$ I am still digesting your algorithm in its simplicity. I comprehend everything up until the while loop. Can you give me some commentary on whats going on here? \$\endgroup\$
    – brndng
    Mar 14, 2019 at 17:21
  • 1
    \$\begingroup\$ First line of while: add hitcount hits to counter total[dom] where dom is the FQDN; Second line: remove first part of FQDN (aaa.example.comexample.com), repeat loop for remaining part. Eventually dom will be just the TLD and when that is removed, there is nothing left, dom == "" and the while conditional fails, ending the loop. If hits is zero, the loop doesn't need to run at all, so we include hits in the conditional. The || 0 part simply initializes the counters if they are unset. \$\endgroup\$ Mar 14, 2019 at 17:32
  • \$\begingroup\$ Oh okay. It seems obvious now. Thanks again! \$\endgroup\$
    – brndng
    Mar 14, 2019 at 18:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.