0

I am trying to loop and filter over a complicated data structure that consists of different arrays and objects like so:

// current tags to filter by
let filterTags = ['Folk', 'Professional']

// return the same data structure that has filtered out any events that dont 
// have SecondaryTag.Title in the filterTags
let filteredEvents = allEvents.filter((el) => {
 return filterTags.includes(el)
});

However, the data structure is made up of complicated arrays and objects making this very complicated.

I have tried all sorts of things which has left me smacking my head into my keyboard. Instead of posting all the ridiculous things I have tried I thought I would post what I was trying to accomplish here in hopes some kind soul will help me out.

let allEvents = [{
    '2018': {
      '03': {
        '31': [
          {
            ID: 1,
            Title: "My Project",
            Date: "2018-02-27",
            SecondaryTag: {
              Title: "Professional"
            }
          }
        ],
        '28': [
          {
            ID: 2,
            Title: "My Project",
            Date: "2018-02-27",
            SecondaryTag: {
              Title: "Business & Professional"
            }
          }
        ]
      },
      '04': {
        '12': [
          {
            ID: 5,
            Title: "My Project2",
            Date: "2018-04-12",
            SecondaryTag: {
              Title: "concert"
            }
          }
        ],
         '2': [
          {
            ID: 7,
            Title: "My Project2",
            Date: "2018-04-12",
            SecondaryTag: {
              Title: "Folk"
            }
          }
        ]
      }
    }
  }];

// This would be the returned filtered structure given the tags
[{
        '2018': {
          '03': {
            '31': [
              {
                ID: 1,
                Title: "My Project",
                Date: "2018-02-27",
                SecondaryTag: {
                  Title: "Professional"
                }
              }
            ],
          },
          '04': {
             '2': [
              {
                ID: 7,
                Title: "My Project2",
                Date: "2018-04-12",
                SecondaryTag: {
                  Title: "Folk"
                }
              }
            ]
          }
        }
      }]

2
  • Welcome to StackOverflow! I have a question, when you're filtering by ['Folk', 'Professional'], do you want Business & Professional? Commented Mar 4, 2018 at 1:02
  • Hi no I don't, thanks for the quick response Commented Mar 4, 2018 at 1:14

1 Answer 1

0

Here's the function in ES6 syntax.

let filtered = {}; //The object or array that will contain the filtered values
filter($toBeFiltered, $filtered, [], 0);  //Run function

function filter(container, obj, keys, level) {
  if (typeof container.SecondaryTag != 'undefined') { //if `SecondaryTag` is an attribute
    let title = container.SecondaryTag.Title; //Get title
    if (filterTags.indexOf(title) > -1) { // If title is what's filtered
      setDeepValue(obj, container, keys, typeof keys[0]); // Set in `filteredEvents` object
    }
    return true; //Stop function
  }

  for (let key in container) {
    let parsed = Array.isArray(container) ? parseInt(key) : key; //Detect Array vs variable
    let copyKeys = [...keys];  //Copy array to prevent key lost
    copyKeys[level] = parsed;
    filter(container[key], obj, copyKeys, level + 1); //Run again
  }
}

function setDeepValue(obj, value, keys, lastType) {  
    let nextType = typeof keys.slice(1, 2)[0]; // Get type of next value
    if (keys.length > 1) { //If not last
    let k = keys.shift(); // Get first value & shift array
    if (obj[k]== null || typeof obj[k] !== 'object'){ // Create object/array if key  doesn't exist
      if (nextType == 'string') {  obj[k] = {}  } // Create array if key is a string
      else if (nextType == 'number') {  obj[k] = [];  } // Create object if key is a number
    }
    setDeepValue(obj[k], value, keys, nextType); // Repeat
  } else { //If value is the last, then set value
    if (lastType == 'string') {  obj[keys[0]] = value;  } // Set value as object if key is a string
    else if (lastType == 'number') {  obj.push(value);  } // Set value as array if key is a number
  }
}

Example using OP's data.

let filterTags = ['Folk', 'Business & Professional'];

let allEvents = [{
    '2018': {
      '03': {
        '31': [
          {
            ID: 1,
            Title: "My Project",
            Date: "2018-02-27",
            SecondaryTag: {
              Title: "Business & Professional"
            }
          }
        ],
        '28': [
          {
            ID: 2,
            Title: "My Project",
            Date: "2018-02-27",
            SecondaryTag: {
              Title: "Business & Professional"
            }
          }
        ]
      },
      '04': {
        '12': [
          {
            ID: 5,
            Title: "My Project2",
            Date: "2018-04-12",
            SecondaryTag: {
              Title: "concert"
            }
          }
        ],
         '2': [
          {
            ID: 7,
            Title: "My Project2",
            Date: "2018-04-12",
            SecondaryTag: {
              Title: "Folk"
            }
          }
        ]
      }
    }
  }];
 
let filtered = {};
  
filter(allEvents, filtered, [], 0);
  
function filter(container, obj, keys, level) {
  
  if (typeof container.SecondaryTag != 'undefined') { //if `SecondaryTag` is an attribute
    let title = container.SecondaryTag.Title; //Get title
    if (filterTags.indexOf(title) > -1) { // If title is what's filtered
      setDeepValue(obj, container, keys, typeof keys[0]); // Set in `filteredEvents` object
    }
    return true; //Stop function
  }
  
  for (let key in container) {
    let parsed = Array.isArray(container) ? parseInt(key) : key; //Used later to detect Array vs Object key
    let copyKeys = [...keys];  //Copy array to prevent key lost
    copyKeys[level] = parsed;
    filter(container[key], obj, copyKeys, level + 1); //Run again
  }
}

function setDeepValue(obj, value, keys, lastType) {  
	let nextType = typeof keys.slice(1, 2)[0]; // Get type of next value
	if (keys.length > 1) { //If not last
    let k = keys.shift(); //Shift Array
    if (obj[k ]== null || typeof obj[k] !== 'object') { //If the key doesn't exist or not array/object
      if (nextType == 'string') {  obj[k] = {}  } //Set to empty object is next key is a string
      else if (nextType == 'number') {  obj[k] = [];  } //Set to empty array is next key is a number
    }
    setDeepValue(obj[k], value, keys, nextType); //Repeat
  } else { //If last key
    /* Neccessary? */
    if (lastType == 'string') {  obj[keys[0]] = value;  } //Set object value if last type is object
    else if (lastType == 'number') {  obj.push(value);  } //Set array value if last type is array
  }
}

console.log(filtered);

This answer makes use of work from Cerbrus.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks heaps for that, works like a charm. 10 points to Hufflepuff

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.