My logic for the problem, using the below as the input.
var input = [['A','B'],1,2,3,['C','D']]
Check first element to see if is an Array or not using Array.isArray(input)
If first element is array, call function, first element ['A,'B'] as argument.
The first element of the nested array is 'A' which is not an array, so push this element into a result array, and shift this element out. Repeat the function call.
When trying to flatten nested arrays using recursion, my input variable to the function keeps getting reassigned, preventing me from calling the function again using the original array. How do I prevent the original input variable from getting reassigned?
I understand this is not the complete solution, however I am stuck at when I shift the first element out of the nested array.
I've gone through step by step with this function, but there must be something I'm missing, another set of eyes would help greatly.
I've also been using my chrome developer tool, set breakpoints to monitor the function step by step.
//Defining original input variable
var input = [['A','B'],1,2,3,['C','D']]
function flat(array){
var result = []
var firstElement = array[0]
//CHECK IF FIRST ELEMENT IS ARRAY OR NOT
if(Array.isArray(firstElement)){
return flat(firstElement)
}
//IF ELEMENT NOT ARRAY, PUSH ELEMENT TO RESULT
else{result.push(firstElement)
array.shift() //removing child element
return (flat(array)) //call function on same array
}
if(array.length===0){return result}
}
First iteration:
firstElement = ['A','B'], Array.isArray(firstElement) would be true, hence call flat(firstElement)
Second Iteration:
firstElement = 'A', Array.isArray(firstElement) is false, so we
1. jump down to push this element into result
2. remove 'A' by using array.shift()
3. Call flat(array), where array is now ['B']
Third Iteration:
firstElement = 'B', Array.isArray(firstElement) is false
1. jump down to push this element into result, result is now only ['B'] since I've reset the result when I recalled the function.
2. remove 'B' by using array.shift(), array is now empty, ->[ ]
3. How can I step out, and use flat() on the original input array?
Your code doesn't consider the following elements if the first element is an array. The solution below uses array.concat(...) to combine both the result of the recursion (going down the tree), but also to combine the results of processing the rest of the list (in the same level). Visualizing the problem as a tree, often helps with recursions IMO:
[] 1 2 3 []
| |
A [] C D
|
B C
So perhaps it is more clear here, that we must both concat the result of the recursion and the result of taking a "step" to the right (recursion again) which would otherwise be a loop iterating the array.
var input = [['A',['B', 'C']],1,2,3,['C','D']]
function flat(array) {
var result = []
if (array.length == 0) return result;
if (Array.isArray(array[0])) {
result = result.concat(flat(array[0])); // Step down
} else {
result.push(array[0]);
}
result = result.concat(flat(array.slice(1))) // Step right
return result;
}
console.log(flat(input));
// ["A", "B", "C", 1, 2, 3, "C", "D"]
This is somewhat analogous to a version with loops:
function flat(array) {
var result = []
for (var i = 0; i < array.length; i++) {
if (Array.isArray(array[i])) {
result = result.concat(flat(array[i]));
} else {
result.push(array[i]);
}
}
return result;
}
EDIT: For debugging purposes, you can track the depth to help get an overview of what happens where:
var input = [['A',['B', 'C']],1,2,3,['C','D']]
function flat(array, depth) {
var result = []
if (array.length == 0) return result;
if (Array.isArray(array[0])) {
result = result.concat(flat(array[0], depth + 1));
} else {
result.push(array[0]);
}
var res1 = flat(array.slice(1), depth);
console.log("Depth: " + depth + " | Concatenating: [" + result + "] with: [" + res1 + "]");
result = result.concat(res1)
return result;
}
console.log(flat(input, 0));
If you want to avoid loops, and I'm considering concating/spreading arrays as loops, you need to pass the result array to your function.
const input = [['A', 'B'], 1, 2, 3, ['C', 'D']]
// Start the function with an empty result array.
function flat(array, result = []) {
if (!array.length)
return result
// Extract first element.
const first = array.shift()
// Call the function with the array element and result array.
if (Array.isArray(first))
flat(first, result)
// Or add the non array element.
else
result.push(first)
// Call the function with the rest of the array and result array.
flat(array, result)
return result
}
console.log(flat(input))
Here is my answer if you are using JavaScript
You can use the below one line code to flatten n level nested Array
let flattendArray = input.flat(Infinity);
Or use this approach using reduce and concat
function flatDeep(arr, d = 1) {
return d > 0 ? arr.reduce((acc, val) => acc.concat(Array.isArray(val) ? flatDeep(val, d - 1) : val), [])
: arr.slice();
};
Refer this link
Related
I see this similar algorithm was posted on stackoverflow, nevertheless I cannot understand, so I decided to post once more.
function capitalizeFirst(arr) {
if (arr.length === 1) {
return [arr[0].toUpperCase()]
}
let res = capitalizeFirst(arr.slice(0, -1))
res.push(arr.slice(arr.length - 1)[0].toUpperCase())
return res
}
console.log(capitalizeFirst(['dog', 'car', 'horse']))
Things I do not understand...
Why it is inside square brackets return [arr[0].toUpperCase()]
why not just return arr[0].toUpperCase()
Why "arr" is getting sliced twice:
here
let res = capitalizeWords(arr.slice(0,-1)
and here
res.push(arr.slice(arr.length-1)[0].toUpperCase())
Overall, I am lost, please help
I see that the OP wants to explain some found code. First, it's not very good code. The function can be restated in a couple easy to read lines.
Here's the not-so-good code annotated (comments *in stars* answer the specific OP questions)
function capitalizeWords(arr) {
// this is the degenerate case: a single item array
if (arr.length === 1) {
return [arr[0].toUpperCase()] // return a *single item array* with the one element capitalized
// incidentally, toUpperCase capitalizes all letters, not only the first, as stated in the OP title
}
// here, length must be zero or > 1. If zero, the remaining code will fail, indexing past 0
// otherwise, if length > 1, this code will run the function on the array minus
// the last element it will return an array (see above) for that last element
let res = capitalizeWords(arr.slice(0, -1))
// this says capitalize the last element.
// it's super clumsy, grabbing the last element by *slicing the array again* just before the end,
// getting that one element from the slice, and using with toUpperCase
// then pushing that uppercase result onto the result array
res.push(arr.slice(arr.length - 1)[0].toUpperCase())
return res
}
Here's a cleanup. First, isolate the capitalization logic and get that tested and correct. It will look like this:
const capitalizeWord = word => word[0].toUpperCase() + word.slice(1);
Next, realize that the most degenerate (elemental) case is capitalizing an empty array. The result of capitalizing an empty array is an empty array.
// something like
return !arr.length ? [] : // ... recursion will go here
When recursing with arrays, we generally say: "do something with the first element, and do the function with the rest of the elements". In JS, it's much more elegant to refer to the "first and rest" than to "all but the last and the last".
// first element (after we've established > 0 length)
arr[0]
// the rest of the elements
arr.slice(1)
Putting this all together...
const capitalizeWord = word => word[0].toUpperCase() + word.slice(1);
function capitalizeWords(arr) {
return arr.length ? [ capitalizeWord(arr[0]), ...capitalizeWords(arr.slice(1))] : [];
}
console.log(capitalizeWords(['dog', 'car', 'horse']))
I would forget about what that code does and concentrate on the steps you need to take to make your function work.
Recursive - so the function needs to call itself but you need to find a way to identify which element you're working on.
You need a way to break out of the recursion when you reach the end of the array.
You need a way to separate out the first letter of an element from all the rest, and update the element with a transformed string.
Here's how I might approach it.
// Pass in the array, and initialise an index
// variable
function capitalizeFirst(arr, index = 0) {
if (!arr.length) return 'Empty array';
// If we're at the end of the array
// return the array
if (index === arr.length) return arr;
// If the element is not empty
if (arr[index].length) {
// Get the first letter, and place all
// the other letters in an array called `rest`
// You can use destructuring here because strings
// are iterable
const [first, ...rest] = arr[index];
// Update the element at the current index
// with the new string making sure you join up `rest`
arr[index] = `${first.toUpperCase()}${rest.join('')}`;
}
// Call the function again increasing the index
return capitalizeFirst(arr, ++index);
}
console.log(capitalizeFirst(['dog', 'car', 'horse']));
console.log(capitalizeFirst([]));
console.log(capitalizeFirst(['dog', '', 'horse']));
console.log(capitalizeFirst(['dog', 'o', 'horse']));
Additional documentation
Destructuring assignment
Rest parameters
Template/string literals
your confusion code
1.let res = capitalizeWords(arr.slice(0,-1)
2.res.push(arr.slice(arr.length-1)[0].toUpperCase())
1.res is an variable array . when this line of code will run let res = capitalizeWords(arr.slice(0,-1)) that means first thing will be done is from your array ['dog', 'car', 'horse'] it will take out the first item that is "dog" and after capitalizeWords function will run and inside capitalizeWords function the argument passed from res is "dog" . and when the function will run if block will run because now arr has one element that is ["dog"] and that will be converted to ["DOG"] . and as like this ['car', 'horse'] this 2 elements will be converted to capital .
but it is a bit complex code to understand as a beginner.
so,you can use my simplify code . i hope you can understand this easily !!
function capitalizeWords(arr) {
if(arr.length === 1) {
return [arr[0].toUpperCase()]
}
let res = []
for (let i of arr){
res.push(i.toUpperCase())
}
return res
}
console.log(capitalizeWords(['dog', 'car', 'horse']))
your another confusion is
return [arr[0].toUpperCase()]
if you write return arr[0].toUpperCase() that means arr[0]="dog" (its a string not an array) . if you just want to print it as a string then you can write arr[0].toUpperCase() :"dog" but if you want to console it as an array then you have to write this : [arr[0].toUpperCase()] :["dog"]
Update
Added some input checking:
if (array.length < 1) return `ERROR Empty Array`;
// Return error message if input is an empty array
if (typeof str === "string" && /[a-z]/.test(str.charAt(0))) {...
/**
* Ignore all non-string data and any string that doesn't start with
* a lower case letter
*/
The code in OP doesn't capitalize each word in an array, it capitalizes every letter of each word. I honestly didn't really try to figure out what's exactly wrong because there's no recursion in the OP anyhow.
Recursion
A function that calls itself within the function (which is itself).
A base condition must be met in order for the function to call itself.
The parameters should change upon each recurse.
The function will cease calling itself once the base condition is no longer true.
In the OP, there's no base condition (see Recursion 2).
In the following example is a recursive function that capitalizes each word of an array.
Pass in the array and index (if index is undefined it defaults to 0)
function capWords(array, index = 0) {...
// array = ["dog", "cat', 'bird'], index = 0
Find the word from the array at the index
let str = array[index];
// str = 'dog'
Get the first letter of that word and capitalize it
let cap = str.charAt(0).toUpperCase();
// cap = "D"
Then concatenate cap to the rest of that word and then reassign the new word to the array at index
array[index] = cap + str.slice(1);
// array = ['Dog', 'cat', 'bird']
If index is less than the length of the array -1...
if (index < array.length - 1) {...
/**
* See Recursion 2
* index = 0, array.length -1 = 2
*/
...return and call capWords(array, index + 1)...
return capWords(array, index + 1)
/**
* See Recursion 1
* array = ['Dog', 'cat', 'bird'], index = 1
* See Recursion 3
*/
...otherwise return array
return array
/**
* See Recursion 4
* After calling capWords() recursively 2 more times, the array is
* returned one more time
* array = ["Dog", "Cat", "Bird"]
*/
function capWords(array, index = 0) {
if (array.length < 1) return `ERROR Empty Array`;
let str = array[index];
if (typeof str === "string" && /[a-z]/.test(str.charAt(0))) {
let cap = str.charAt(0).toUpperCase();
array[index] = cap + str.slice(1);
}
if (index < array.length - 1) {
return capWords(array, index + 1);
}
return array;
}
console.log(capWords(['dog', 'cat', 'bird'], 0));
console.log(capWords(['dog', '', 'bird']));
console.log(capWords([2, 'cat', 'bird'], 0));
console.log(capWords(['dog', 'cat', {}], 0));
console.log(capWords([]));
You just forgot to select the First letter charAt(0) and to add more logic to connect the first letter with the other part of the word array[0].charAt(0).toUpperCase() + array[0].slice(1).toLowerCase();
The same situation when you are recursively pushing every word into an array.
function capitalizeFirst (array){
if (array.length === 1) {
return [array[0].charAt(0).toUpperCase() + array[0].slice(1).toLowerCase()];
}
var word = capitalizeFirst(array.slice(0, -1));
word.push(array.slice(array.length-1)[0].charAt(0).toUpperCase() +
array.slice(array.length-1)[0].slice(1).toLowerCase());
return word;
}
function twoSum(numbers, target) {
var result = [];
numbers.forEach(function(value, index) {
return numbers.forEach(function(value2, index2) {
if (value + value2 === target) {
result.push(index, index2);
return result;
}
})
})
return result;
}
twoSum([1, 2, 3], 4);
//Output - [ 0, 2, 1, 1, 2, 0 ]
Hi - I'm working on a particular codewars problem and I seem to be misunderstanding the usage of return for callback functions. In this particular problem I just want to find the first two sums of numbers that equal the target and push those index values into result. I don't want to keep iterating through my function after that - meaning I only want the first pair that's found. My current output gives me all the index values for the target sum. Not just the first 2. It seems I am not using my return commands correctly. My current line of thought is that return result returns a value to my nested callback of parameters (value2, index2). That result is then returned to my outside function of (value,index). Why does my loop not cease after that return?
It doesn't end because .forEach cannot be terminated early. forEach is not paying any attention to the values you return. If you want to terminate early you'll need to use a different approach.
If you want to stick with array methods, there are .some and .every. The former continues until a run of your function returns true, and the latter continues until a run of your function returns false. These are meant for doing compound OR's and compound AND's with every element of the array, but they can kinda be used for your case too.
numbers.some(function(value, index) {
return numbers.some(function(value2, index2) {
if (value + value2 === target) {
result.push(index, index2);
return true;
}
return false;
})
})
Or you could use a standard for loop, with the break keyword when you want to stop the loop.
Beside the not working return statement for a outer function, you need to take a different approach which uses only a single loop and an object for storing the index of the found value.
function twoSum(numbers, target) {
var indices = {};
for (let i = 0; i < numbers.length; i++) {
const number = numbers[i];
if (number in indices) return [indices[number], i];
indices[target - number] = i;
}
}
console.log(twoSum([1, 2, 3], 4));
I have the following function, which pretty much does what it supposed to, but I would like to understand exactly what it does on each steps of its loop.
Could you please take a look to the function below and give me a clear explanation commenting each step or the Filter and IndexOf methods?
Thank you very much in advance.
var arr = [6,2,6,8,9,9,9,4,5];
var unique = function(){
return arr.filter(function(e, i, a) {
return i === a.indexOf(e);
})
}
unique();
indexOf returns the first index of an element in an array:
[1,2,2,3].indexOf(2); // 1
So if you use filter as in your example when it gets to the second occurance of an element the index (i in your example) will not be equal to the value returned by indexOf and be dropped. In my array above the second 2 is at position 2 which obviously doesn't strictly equal the one returned by indexOf.
[1,2,2,3].filter((value, index, array) => array.indexOf(value) === index);
// first iteration: value is 1, index is 0, indexOf is 0 0===0 keep 1
// second: value is 2, index is 1, indexOf is 1, 1===1 keep 2
// third: value is 2, index is 2, indexOf is 1, 1===2 false! toss 2
// etc.
The end effect is that any duplicate elements get dropped from the copy returned by filter. And it is a copy, the original array is not mutated.
EDIT
I should probably mention that recent versions of JavaScript give us a better way:
let arrayWithDupes = [1,2,2,3];
let uniq = Array.from(new Set(arrayWithDupes)); // [1,2,3]
If log the values like:
var arr = [6,2,6,8,9,9,9,4,5];
var unique = function(){
return arr.filter(function(e, i, a) {
console.log('e: ' + e);
console.log('i: ' + i);
console.log('a: ' + a);
return i === a.indexOf(e);
})
}
var unq = unique();
console.log(unq);
you will get:
"e: 6"
"i: 0"
"a: 6,2,6,8,9,9,9,4,5"
and so on...
e = current element from array, i = index of the array, a = array source;
Filer function: "The filter() method creates an array filled with all array elements that pass a test (provided as a function)."
indexOf: "The indexOf() method searches the array for the specified item, and returns its position."
Suppose I've got a nested array:
// 3rd element in sub-array indicates a number of repeats
var list = [["a","b",1],["a","d",1],["a","b",1],["c","d",1]];
Task is to remove identical sub-arrays and increase number in single unique sub-array, which would indicate the number of repeats, so that above example would transform into smth like:
[["a","b",2],["a","d",1],["c","d",1]]
What would be the most efficient way to achieve this?
Currently I'm trying smth like this:
var list = new Array();
// Sort by second element
list.sort(function(a,b) {
return a[1] > b[1];
});
function collateList(element,index,array){
// if 1st&2nd element of subarray equals to 1st&2nd element of next subarray
if (array[index[0]]==array[index[0]+1] && array[index[1]]==array[index[1]+1]){
// increase 3rd element of subarray by 1
array[index[2]] = array[index[2]+1];
// remove next element from an array
array.splice((index+1),1);
}
}
list.forEach(collateList);
Let us first define the function determining if two subarrays are to be combined, in this case that their first two values are the same:
function match(e1, e2) { return e1[0]===e2[0] && e1[1]===e2[1]; }
Now let us define a function which finds a matching element in an array, based on a matching function, and returns its index. This is the same as Array.prototype.findIndex, were it defined.
function find(a, v, fn) {
for (i in a) { if (fn(v, a[i])) {return i;} }
return -1;
}
Now we feed the input through a reduce to create an new array with counts updated and duplicates removed:
list.reduce( // Boil down array into a result
function(result, elt) { // by taking each element
var prev = find(result, elt, match); // and looking for it in result so far.
if (prev !== -1) { // If found
result[prev][2]++; // increment previous occurrence;
} else { // otherwise
result.push(elt); // include as is in the result.
}
return result; // Use this result for next iteration.
},
[] // Start off with an empty array.
)
I have an array of objects in javascript. I use jquery.
How do i get the first element in the array? I cant use the array index - as I assign each elements index when I am adding the objects to the array. So the indexes arent 0, 1, 2 etc.
Just need to get the first element of the array?
If you don't use sequentially numbered elements, you'll have to loop through until you hit the first one:
var firstIndex = 0;
while (firstIndex < myarray.length && myarray[firstIndex] === undefined) {
firstIndex++;
}
if (firstIndex < myarray.length) {
var firstElement = myarray[firstIndex];
} else {
// no elements.
}
or some equivalently silly construction. This gets you the first item's index, which you might or might not care about it.
If this is something you need to do often, you should keep a lookaside reference to the current first valid index, so this becomes an O(1) operation instead of O(n) every time. If you're frequently needing to iterate through a truly sparse array, consider another data structure, like keeping an object alongside it that back-maps ordinal results to indexes, or something that fits your data.
The filter method works with sparse arrays.
var first = array.filter(x => true)[0];
Have you considered:
function getFirstIndex(array){
var result;
if(array instanceof Array){
for(var i in array){
result = i;
break;
}
} else {
return null;
}
return result;
}
?
And as a way to get the last element in the array:
function getLastIndex(array){
var result;
if(array instanceof Array){
result = array.push("");
array.pop;
}
} else {
return null;
}
return result;
}
Neither of these uses jquery.
Object.keys(array)[0] returns the index (in String form) of the first element in the sparse array.
var array = [];
array[2] = true;
array[5] = undefined;
var keys = Object.keys(array); // => ["2", "5"]
var first = Number(keys[0]); // => 2
var last = Number(keys[keys.length - 1]); // => 5
I was also facing a similar problem and was surprised that no one has considered the following:
var testArray = [];
testArray [1245]= 31;
testArray[2045] = 45;
for(index in testArray){
console.log(index+','+testArray[index])
}
The above will produce
1245,31
2045,45
If needed you could exist after the first iteration if all that was required but generally we need to know where in the array to begin.
This is a proposal with ES5 method with Array#some.
The code gets the first nonsparse element and the index. The iteration stops immediately with returning true in the callback:
var a = [, , 22, 33],
value,
index;
a.some(function (v, i) {
value = v;
index = i;
return true;
});
console.log(index, value);
If you find yourself needing to do manipulation of arrays a lot, you might be interested in the Underscore library. It provides utility methods for manipulating arrays, for example compact:
var yourArray = [];
yourArray[10] = "foo";
var firstValue = _.compact(yourArray)[0];
However, it does sound like you are doing something strange when you are constructing your array. Perhaps Array.push would help you out?