JS data flattening

Time:2022-7-6

Recently, I found some excellent articles on data flattening, which I share with you here, hoping to help you

What is flattening

The flattening of an array is to convert a nested array with multiple layers (nesting can be any number of layers) into an array with only one layer.

For example, suppose a function named flatten can flatten the array, and the effect will be as follows:

var arr = [1, [2, [3, 4]]];
console.log(flatten(arr)) // [1, 2, 3, 4]

Circular array + recursion

Implementation idea: loop the array. If there is an array in the data, recursively call the flat function (flatten with the for loop), connect with concat, and finally return the result;

 function flatten(arr){
     var result = [];
     for(var i = 0, len = arr.length; i < len; i++){
         if(Array.isArray(arr[i])){
             result = result.concat(flatten(arr[i]));
         }else{
             result.push(arr[i]);
         }
     }
     return result;
 }

flatten(arr)   // [1,2,3,4]

recursion

At the beginning, we can think of nothing more than looping array elements. If it is still an array, call the method recursively:

	var arr = [1, [2, [3, 4]]];
	
	function flatten(arr) {
	    var result = [];
	    for (var i = 0, len = arr.length; i < len; i++) {
	        if (Array.isArray(arr[i])) {
	            result = result.concat(flatten(arr[i]))
	        }
	        else {
	            result.push(arr[i])
	        }
	    }
	    return result;
	}


console.log(flatten(arr))

tostring

If the elements of the array are all numbers, we can consider using the toString method, because:

[1, [2, [3, 4]]].toString() // “1,2,3,4”
Calling the toString method returns a comma separated flat string. At this time, we can split it and convert it to a number to realize flattening

//Method 2
	var arr = [1, [2, [3, 4]]];
	
	function flatten(arr) {
	    return arr.toString().split(',').map(function(item){
	        return +item
	    })
	}
	
	console.log(flatten(arr))

However, the scenarios used in this method are very limited. If the array is [1, ‘1’, 2, ‘2], this method will produce wrong results.

reduce

Since the array is processed and finally a value is returned, we can consider using reduce to simplify the code:

//Method 3
	var arr = [1, [2, [3, 4]]];
	
	function flatten(arr) {
	    return arr.reduce(function(prev, next){
	        return prev.concat(Array.isArray(next) ? flatten(next) : next)
	    }, [])
	}
	
	console.log(flatten(arr))

ES6 adds an extension operator to get all the traversable attributes of the parameter object and copy them to the current object:

var arr = [1, [2, [3, 4]]];
console.log([].concat(…arr)); // [1, 2, [3, 4]]
We can only flatten one layer with this method, but keep thinking along this method, and we can write this method:

	var arr = [1, [2, [3, 4]]];

		function flatten(arr) {
		
		    while (arr.some(item => Array.isArray(item))) {
		        arr = [].concat(...arr);
		    }
		
		    return arr;
		}
		
		console.log(flatten(arr))

undercore

So how to write an abstract flat function to facilitate our development? It’s time for us to copy underscore again~

The source code and comments are given directly here, but it should be noted that the flatten function here is not the final _ Flatten, in order to facilitate multiple API calls, more configurations are made for flat.

/**
	 *Flatten array
	 *@param {array} input the array to be processed
	 *Whether @param {boolean} shallow is only flat by one layer
	 *Whether @param {boolean} strict strictly handles elements is explained below
	 *@param {array} output this is a parameter passed to facilitate recursion
	 */

Source code address: https://github.com/jashkenas/underscore/blob/master/underscore.js#L528

function flatten(input, shallow, strict, output) {

    //Output is used when recursion is used
    output = output || [];
    var idx = output.length;

    for (var i = 0, len = input.length; i < len; i++) {

        var value = input[i];
        //If it is an array, process it
        if (Array.isArray(value)) {
            //If there is only one flat layer, traverse the array and fill in the output accordingly
            if (shallow) {
                var j = 0, len = value.length;
                while (j < len) output[idx++] = value[j++];
            }
            //If it is all flat, recurse, pass in the processed output, and then process the output in recursion
            else {
                flatten(value, shallow, strict, output);
                idx = output.length;
            }
        }
        //It is not an array. Judge whether to skip without processing or put it into output according to the value of strict
        else if (!strict){
            output[idx++] = value;
        }
    }

    return output;

}

Explain strict. In the code, we can see that when traversing array elements, if the element is not an array, the result of strict negation will be judged. If strict is set to true, no processing will be skipped, which means that non array elements can be filtered, for example:

var arr = [1, 2, [3, 4]];
console.log(flatten(arr, true, true)); // [3, 4]
So what is the use of setting strict? Don’t worry, let’s first look at the results corresponding to various values of shallow and strct:

Shallow true + strict false: normal flat one layer
Shallow false + strict false: normal flattening of all layers
Shallow true + strict true: remove non array elements
Share false + strict true: returns a []
Let’s look at which methods in underscore call the basic function flatten:

_.flatten

The first is _ flatten:

	_.flatten = function(array, shallow) {
	    return flatten(array, shallow, false);
	};

In normal flattening, we do not need to remove non array elements.

_.union

This function passes in multiple arrays, and then returns the union of the passed in arrays,

for instance:

_.union([1, 2, 3], [101, 2, 1, 10], [2, 1]);
=> [1, 2, 3, 101, 10]
If the parameter passed in is not an array, it will be skipped:

_.union([1, 2, 3], [101, 2, 1, 10], 4, 5);
=> [1, 2, 3, 101, 10]
To achieve this effect, we can flatten all the passed in arrays and then de duplicate them, because only arrays can be passed in. At this time, we can directly set strict to true to skip the passed in non array elements.

//For unique, you can check the array de duplication of JavaScript topics https://github.com/mqyqingfeng/Blog/issues/27

function unique(array) {
   return Array.from(new Set(array));
}

_.union = function() {
    return unique(flatten(arguments, true, true));
}

_.difference

Does it feel that tossing strict is a little useful? Let’s see another _ difference:

The syntax is:

_.difference(array, *others)

The effect is to get the elements from the array that do not exist in multiple other arrays. Follow _ Like Union, elements that are not arrays are excluded.

for instance:

_.difference([1, 2, 3, 4, 5], [5, 2, 10], [4], 3);
=> [1, 3]
The implementation method is also very simple. Flatten the array of others and filter out the values in the array that are not in the flattened array:

function difference(array, ...rest) {

    rest = flatten(rest, true, true);

    return array.filter(function(item){
        return rest.indexOf(item) === -1;
    })
}

If it helps you, you are welcome to pay attention. I will update the technical documents regularly, and we will discuss and learn together and make progress together.

 JS data flattening

 

Recommended Today

Some practical exercises for Kubernetes beginners (5)

The first four articles in this hands-on practice series: Some practical exercises for Kubernetes beginners (1) Some practical exercises for Kubernetes beginners (2) Some practical exercises for Kubernetes beginners (3) Some practical exercises for Kubernetes beginners (4) This article continues our Kubernetes hands-on exercise tour. Exercise 1 – Inject a configmap into a pod via […]