How to count instances of numbers in an array using recursion? - javascript

I try to write a function (called: tally) using recursion (part of the exercise) to scan through an array of numbers and return an object with the numbers as key and the number of instances as value.
Example:
tally([2,3,4,5,5,5,5,5,5,5,6,7,,6,7,6,7,5,4,3,4,5,5,6])
//{2: 1, 3: 2, 4: 3, 5: 10, 6: 4, 7: 3}
I created the framework but i am not sure about the syntax to make it work:
function tally(arr) {
var obj = {}
if (/*check if object ('obj') has a key corresponding to the array element*/) {
//increase key's value by onee
} else {
//add key with value of 1
}
return obj
};
Any hints to complete the recursion function above? Please try to stick to my structure in your answers as much as possible since this is part of an exercise.

Here you are:
function tally(arr) {
if (arr.length == 0) {
return {}
}
var value = arr.pop()
var obj = tally(arr)
if (value in obj) {
obj[value] += 1
} else {
obj[value] = 1
}
return obj
};
EDIT:
It can also be done using slice() instead of pop():
function tally(arr) {
if (arr.length == 0) {
return {}
}
var value = arr[0]
var obj = tally(arr.slice(1))
if (value in obj) {
obj[value] += 1
} else {
obj[value] = 1
}
return obj
};

Using extra parameter for an index, i, the result, r -
const plus1 = (k = "", r = {}) =>
( k in r
? r[k] += 1
: r[k] = 1
, r
)
const tally = (a = [], i = 0, r = {}) =>
i >= a.length
? r
: tally
( a
, i + 1
, plus1(a[i], r)
)
console.log(tally([2,3,4,5,5,5,5,5,5,5,6,7,,6,7,6,7,5,4,3,4,5,5,6]))
Output
{
"2": 1,
"3": 2,
"4": 3,
"5": 10,
"6": 4,
"7": 3,
"undefined": 1
}

ok, so you are asked to do a recursion just for the sake of it.
This could be done (albeit is hacky) passing an extra parameter to tally. When you declare a function in vanilla js you can actually feed it extra stuff. So, in each recursion, pass obj as a second parameter:
EDIT
Thanks #Bergi, you're right. I'll edit the code
function tally(arr) {
let obj = arguments.length>1? arguments[1] : {};
if(arr.length===0) {
return obj;
}
let next_number=arr.pop();
obj[next_number]=obj[next_number]||0;
obj[next_number]++;
return tally(arr,obj);
};
let inputArr = [2,3,4,5,5,5,5,5,5,5,6,7,6,7,6,7,5,4,3,4,5,5,6],
outputObj=tally(inputArr);
console.log(outputObj);
console.log({outputEmpty:tally([])});

I am not sure how to guide you to an answer without giving it away entirely, but this is what I would recommend. (There are some problems such as you destroy arr in the process that you may want to consider)
function tally(arr, obj) {
// if the length is zero we've gone through every value
if(arr.length === 0)
return obj
// create obj if we didn't provide it
if(obj === undefined)
obj = {}
// pull the last value from arr
let val = arr.pop()
if (/*check if object ('obj') has a key corresponding to the array element*/) {
//increase key's value by onee
} else {
//add key with value of 1
}
// move onto the next value
return tally(arr,obj)
}
EDIT: took #Bergi's input

Related

how can I do this better in javascript: add a pair (key, value) to a hash while iterating?

I wrote this solution that works, although I would like to verify with the community if there is a better way to add a pair (key, value) to a hash while I am iterating. I have to create a new function with name addPairToHashAndReturnIt as an auxiliar function.
The challenge is the following:
Create a function countBy that accepts an array and a callback, and returns an object. countBy will iterate through the array and perform the callback on each element. Each return value from the callback will be saved as a key on the object. The value associated with each key will be the number of times that particular return value was returned.
function countBy(array, callback) {
return array.reduce(
(acc, each) =>
(!acc[callback(each)] && addPairToHashAndReturnIt(acc, callback(each), each)) ||
((acc[callback(each)] += 1) && acc),
{}
);
}
const addPairToHashAndReturnIt = (hash, key, value) => {
hash[key] = 1;
return hash;
};
function evenOrOddNumber(num) {
if (num % 2 === 0) return 'even';
else return 'odd';
};
console.log(
countBy([1, 2, 3, 4, 5], evenOrOddNumber)
); // should log: { odd: 3, even: 2 }
So, what do you think about this solution?
Simplify your logic, don't make it unnecessarily complicated.
We aren't wanting to overwrite the value of hash[value] with 5 (for instance), we're just wanting to increment hash[value] every time we get a match. The form will look like hash[value]++, not hash[value] = item.
See also: Is Reduce() Bad?
const evenOrOddNumber = (num) => (num % 2 == 0)
? 'even'
: 'odd';
const countBy = (array, callback) => {
// store counts
const counts = {};
array.forEach(item => {
// store callback result, previous count
const val = callback(item);
const prevCount = counts[val] || 0;
// increment count
counts[val] = prevCount + 1;
});
return counts;
}
console.log(
countBy([1, 2, 3, 4, 5], evenOrOddNumber)
); // should log: { odd: 3, even: 2 }

How to access Javascript object by path? [duplicate]

This question already has answers here:
Accessing nested JavaScript objects and arrays by string path
(44 answers)
Closed 7 years ago.
I'm temporarily stuck with what appears to be a very simple JavaScript problem, but maybe I'm just missing the right search keywords!
Say we have an object
var r = { a:1, b: {b1:11, b2: 99}};
There are several ways to access the 99:
r.b.b2
r['b']['b2']
What I want is to be able to define a string
var s = "b.b2";
and then access the 99 using
r.s or r[s] //(which of course won't work)
One way is to write a function for it that splits the string on dot and maybe recursively/iteratively gets the property. But is there any simpler/more efficient way? Anything useful in any of the jQuery APIs here?
Here's a naive function I wrote a while ago, but it works for basic object properties:
function getDescendantProp(obj, desc) {
var arr = desc.split(".");
while(arr.length && (obj = obj[arr.shift()]));
return obj;
}
console.log(getDescendantProp(r, "b.b2"));
//-> 99
Although there are answers that extend this to "allow" array index access, that's not really necessary as you can just specify numerical indexes using dot notation with this method:
getDescendantProp({ a: [ 1, 2, 3 ] }, 'a.2');
//-> 3
split and reduce while passing the object as the initalValue
Update
(thanks to comment posted by TeChn4K)
With ES6 syntax, it is even shorter
var r = { a:1, b: {b1:11, b2: 99}};
var s = "b.b2";
var value = s.split('.').reduce((a, b) => a[b], r);
console.log(value);
Old version
var r = { a:1, b: {b1:11, b2: 99}};
var s = "b.b2";
var value = s.split('.').reduce(function(a, b) {
return a[b];
}, r);
console.log(value);
You can use lodash get() and set() methods.
Getting
var object = { 'a': [{ 'b': { 'c': 3 } }] };
_.get(object, 'a[0].b.c');
// → 3
Setting
var object = { 'a': [{ 'b': { 'c': 3 } }] };
_.set(object, 'a[0].b.c', 4);
console.log(object.a[0].b.c);
// → 4
If it's possible in your scenario that you could put the entire array variable you're after into a string you could use the eval() function.
var r = { a:1, b: {b1:11, b2: 99}};
var s = "r.b.b2";
alert(eval(s)); // 99
I can feel people reeling in horror
Extending #JohnB's answer, I added a setter value as well. Check out the plunkr at
http://plnkr.co/edit/lo0thC?p=preview
function getSetDescendantProp(obj, desc, value) {
var arr = desc ? desc.split(".") : [];
while (arr.length && obj) {
var comp = arr.shift();
var match = new RegExp("(.+)\\[([0-9]*)\\]").exec(comp);
// handle arrays
if ((match !== null) && (match.length == 3)) {
var arrayData = {
arrName: match[1],
arrIndex: match[2]
};
if (obj[arrayData.arrName] !== undefined) {
if (typeof value !== 'undefined' && arr.length === 0) {
obj[arrayData.arrName][arrayData.arrIndex] = value;
}
obj = obj[arrayData.arrName][arrayData.arrIndex];
} else {
obj = undefined;
}
continue;
}
// handle regular things
if (typeof value !== 'undefined') {
if (obj[comp] === undefined) {
obj[comp] = {};
}
if (arr.length === 0) {
obj[comp] = value;
}
}
obj = obj[comp];
}
return obj;
}
This is the simplest i could do:
var accessProperties = function(object, string){
var explodedString = string.split('.');
for (i = 0, l = explodedString.length; i<l; i++){
object = object[explodedString[i]];
}
return object;
}
var r = { a:1, b: {b1:11, b2: 99}};
var s = "b.b2";
var o = accessProperties(r, s);
alert(o);//99
you could also do
var s = "['b'].b2";
var num = eval('r'+s);
Here is an extension of Andy E's code, that recurses into arrays and returns all values:
function GetDescendantProps(target, pathString) {
var arr = pathString.split(".");
while(arr.length && (target = target[arr.shift()])){
if (arr.length && target.length && target.forEach) { // handle arrays
var remainder = arr.join('.');
var results = [];
for (var i = 0; i < target.length; i++){
var x = this.GetDescendantProps(target[i], remainder);
if (x) results = results.concat(x);
}
return results;
}
}
return (target) ? [target] : undefined; //single result, wrap in array for consistency
}
So given this target:
var t =
{a:
{b: [
{'c':'x'},
{'not me':'y'},
{'c':'z'}
]
}
};
We get:
GetDescendantProps(t, "a.b.c") === ["x", "z"]; // true
I don't know a supported jQuery API function but I have this function:
var ret = data; // Your object
var childexpr = "b.b2"; // Your expression
if (childexpr != '') {
var childs = childexpr.split('.');
var i;
for (i = 0; i < childs.length && ret != undefined; i++) {
ret = ret[childs[i]];
}
}
return ret;
I've extended Andy E's answer, so that it can also handle arrays:
function getDescendantProp(obj, desc) {
var arr = desc.split(".");
//while (arr.length && (obj = obj[arr.shift()]));
while (arr.length && obj) {
var comp = arr.shift();
var match = new RegExp("(.+)\\[([0-9]*)\\]").exec(comp);
if ((match !== null) && (match.length == 3)) {
var arrayData = { arrName: match[1], arrIndex: match[2] };
if (obj[arrayData.arrName] != undefined) {
obj = obj[arrayData.arrName][arrayData.arrIndex];
} else {
obj = undefined;
}
} else {
obj = obj[comp]
}
}
return obj;
}
There are probably more efficient ways to do the Regex, but it's compact.
You can now do stuff like:
var model = {
"m1": {
"Id": "22345",
"People": [
{ "Name": "John", "Numbers": ["07263", "17236", "1223"] },
{ "Name": "Jenny", "Numbers": ["2", "3", "6"] },
{ "Name": "Bob", "Numbers": ["12", "3333", "4444"] }
]
}
}
// Should give you "6"
var x = getDescendantProp(model, "m1.People[1].Numbers[2]");
Performance tests for Andy E's, Jason More's, and my own solution are available at http://jsperf.com/propertyaccessor. Please feel free to run tests using your own browser to add to the data collected.
The prognosis is clear, Andy E's solution is the fastest by far!
For anyone interested, here is the code for my solution to the original question.
function propertyAccessor(object, keys, array) {
/*
Retrieve an object property with a dot notation string.
#param {Object} object Object to access.
#param {String} keys Property to access using 0 or more dots for notation.
#param {Object} [array] Optional array of non-dot notation strings to use instead of keys.
#return {*}
*/
array = array || keys.split('.')
if (array.length > 1) {
// recurse by calling self
return propertyAccessor(object[array.shift()], null, array)
} else {
return object[array]
}
}
Short answer: No, there is no native .access function like you want it. As you correctly mentioned, you would have to define your own function which splits the string and loops/checks over its parts.
Of course, what you always can do (even if its considered bad practice) is to use eval().
Like
var s = 'b.b2';
eval('r.' + s); // 99
Here is a a little better way then #andy's answer, where the obj (context) is optional, it falls back to window if not provided..
function getDescendantProp(desc, obj) {
obj = obj || window;
var arr = desc.split(".");
while (arr.length && (obj = obj[arr.shift()]));
return obj;
};

recursively putting array elements in their own array

I'm trying to create a function that puts each array element in its own array, recursively.
I think my base case is correct, but my recursive call doesn't appear to be working. any insight?
function ownList(arr){
if (arr.length === 1) {
arr[0] = [arr[0]];
return;
} else {
return arr[0].concat(ownList(arr.slice(1)));
}
}
var arr = [1,2,3]
console.log(ownList(arr))// returns []
//should return [[1],[2],[3]]
Here I'm trying to put each pair in it's own list (recursive only). This code below is correct (update)
function ownListPair(arr){
if (arr.length === 0)
return arr;
else if(arr.length === 1)
return [[arr[0], 0]];
else
return [[arr[0], arr[1]]].concat(ownListPair(arr.slice(2)));
}
// var arr = [3,6,8,1,5]
var arr = [2,7,8,3,1,4]
//returns [ [ 2, 7 ], [ 8, 3 ], [ 1, 4 ]]
console.log(ownListPair(arr))
I prefer this solution for several reasons:
function ownList(a) {
return a.length == 0
? []
: [[a[0]]].concat(ownList(a.slice(1)))
}
It's shorter and more concise
It works for empty arrays as well
The actual wrapping happens only once in the last line. Treating length == 1 separately -- as suggested by others -- is not necessary.
It would more appropriate to make a length of 0 be the null case. Then you just have to get the brackets right. The thing on the left side of the concat should be an array consisting of the array containing the first element.
function ownList(arr) {
return arr.length ? [[arr[0]]].concat(ownList(arr.slice(1))) : [];
}
Here's an alternative, take your pick:
function ownList(arr) {
return arr.length ? [[arr.shift()]] . concat(ownList(arr)) : [];
}
Using a bit of ES6 magic for readability:
function ownList([head, ...tail]) {
return head === undefined ? [] : [[head]] . concat(ownList(tail));
}
Here the [head, ...tail] is using parameter destructuring which pulls the argument apart into its first element (head) and an array of remaining ones (tail).
Instead of concat you could also use the array constructor:
function ownList([head, ...tail]) {
return head === undefined ? [] : Array([head], ...ownList(tail));
}
I think your basic assumption is wrong. What you need to do is check if each item in the array is an array, if not just add the item to the new array, if so have the function run itself on the array item.
That is recursion.
This code does that kind of recursion...
function ownList(arr)
{
var newArr = [];
var length = arr.length;
for (var i = 0; i < length; i++) {
if (typeof(arr[i]) === 'object') {
newArr.push(ownList(arr[i]));
continue;
}
newArr.push([arr[i]]);
}
return newArr;
}
var arr = [1, 2, 3];
console.log(ownList(arr));
Would something like this work:
var arr = [1, 2, 3, ["a", "b", "c", ["str"]]],
result = [];
function flatten(input){
input.forEach(function(el){
if(Array.isArray(el)){
flatten(el)
}else{
result.push([el]);
}
});
}
flatten(arr);
console.log(JSON.stringify(result));
//[[1],[2],[3],["a"],["b"],["c"],["str"]]
JSBIN
Edit:
var result = [];
function flatten(input){
if (input.length === 0){
console.log( "result", result ); //[[1],[2],[3],["a"],["b"],["c"],["str"]]
return;
}
//if zeroth el of input !array, push to result
if (!Array.isArray(input[0])){
result.push(input.splice(0, 1));
flatten(input);
}else{
flatten(input[0]); //else, give input[0] back to flatten
}
}
window.onload = function(){
var arr = [1, 2, 3, ["a", "b", "c", ["str"]]];
flatten(arr);
}
JSBIN
After struggling through this today, turns out that this works :)
function ownList(arr){
//base case:
if (arr.length === 1) {
return [arr];
}
//recurse
//have to do two brackets here --> (arr.slice(0,1)) since length > 1
return [arr.slice(0,1)].concat(ownList(arr.slice(1)));
}
var arr = [1,2,3]
console.log(ownList(arr))// returns [[1],[2],[3]]

How to sum the values of a JavaScript object?

I'd like to sum the values of an object.
I'm used to python where it would just be:
sample = { 'a': 1 , 'b': 2 , 'c':3 };
summed = sum(sample.itervalues())
The following code works, but it's a lot of code:
function obj_values(object) {
var results = [];
for (var property in object)
results.push(object[property]);
return results;
}
function list_sum( list ){
return list.reduce(function(previousValue, currentValue, index, array){
return previousValue + currentValue;
});
}
function object_values_sum( obj ){
return list_sum(obj_values(obj));
}
var sample = { a: 1 , b: 2 , c:3 };
var summed = list_sum(obj_values(a));
var summed = object_values_sum(a)
Am i missing anything obvious, or is this just the way it is?
It can be as simple as that:
const sumValues = obj => Object.values(obj).reduce((a, b) => a + b, 0);
Quoting MDN:
The Object.values() method returns an array of a given object's own enumerable property values, in the same order as that provided by a for...in loop (the difference being that a for-in loop enumerates properties in the prototype chain as well).
from Object.values() on MDN
The reduce() method applies a function against an accumulator and each value of the array (from left-to-right) to reduce it to a single value.
from Array.prototype.reduce() on MDN
You can use this function like that:
sumValues({a: 4, b: 6, c: -5, d: 0}); // gives 5
Note that this code uses some ECMAScript features which are not supported by some older browsers (like IE). You might need to use Babel to compile your code.
You could put it all in one function:
function sum( obj ) {
var sum = 0;
for( var el in obj ) {
if( obj.hasOwnProperty( el ) ) {
sum += parseFloat( obj[el] );
}
}
return sum;
}
var sample = { a: 1 , b: 2 , c:3 };
var summed = sum( sample );
console.log( "sum: "+summed );
For fun's sake here is another implementation using Object.keys() and Array.reduce() (browser support should not be a big issue anymore):
function sum(obj) {
return Object.keys(obj).reduce((sum,key)=>sum+parseFloat(obj[key]||0),0);
}
let sample = { a: 1 , b: 2 , c:3 };
console.log(`sum:${sum(sample)}`);
But this seems to be way slower: jsperf.com
If you're using lodash you can do something like
_.sum(_.values({ 'a': 1 , 'b': 2 , 'c':3 }))
Now you can make use of reduce function and get the sum.
const object1 = { 'a': 1 , 'b': 2 , 'c':3 }
console.log(Object.values(object1).reduce((a, b) => a + b, 0));
A regular for loop is pretty concise:
var total = 0;
for (var property in object) {
total += object[property];
}
You might have to add in object.hasOwnProperty if you modified the prototype.
Honestly, given our "modern times" I'd go with a functional programming approach whenever possible, like so:
const sumValues = (obj) => Object.keys(obj).reduce((acc, value) => acc + obj[value], 0);
Our accumulator acc, starting with a value of 0, is accumulating all looped values of our object. This has the added benefit of not depending on any internal or external variables; it's a constant function so it won't be accidentally overwritten... win for ES2015!
Any reason you're not just using a simple for...in loop?
var sample = { a: 1 , b: 2 , c:3 };
var summed = 0;
for (var key in sample) {
summed += sample[key];
};
http://jsfiddle.net/vZhXs/
let prices = {
"apple": 100,
"banana": 300,
"orange": 250
};
let sum = 0;
for (let price of Object.values(prices)) {
sum += price;
}
alert(sum)
I am a bit tardy to the party, however, if you require a more robust and flexible solution then here is my contribution. If you want to sum only a specific property in a nested object/array combo, as well as perform other aggregate methods, then here is a little function I have been using on a React project:
var aggregateProperty = function(obj, property, aggregate, shallow, depth) {
//return aggregated value of a specific property within an object (or array of objects..)
if ((typeof obj !== 'object' && typeof obj !== 'array') || !property) {
return;
}
obj = JSON.parse(JSON.stringify(obj)); //an ugly way of copying the data object instead of pointing to its reference (so the original data remains unaffected)
const validAggregates = [ 'sum', 'min', 'max', 'count' ];
aggregate = (validAggregates.indexOf(aggregate.toLowerCase()) !== -1 ? aggregate.toLowerCase() : 'sum'); //default to sum
//default to false (if true, only searches (n) levels deep ignoring deeply nested data)
if (shallow === true) {
shallow = 2;
} else if (isNaN(shallow) || shallow < 2) {
shallow = false;
}
if (isNaN(depth)) {
depth = 1; //how far down the rabbit hole have we travelled?
}
var value = ((aggregate == 'min' || aggregate == 'max') ? null : 0);
for (var prop in obj) {
if (!obj.hasOwnProperty(prop)) {
continue;
}
var propValue = obj[prop];
var nested = (typeof propValue === 'object' || typeof propValue === 'array');
if (nested) {
//the property is an object or an array
if (prop == property && aggregate == 'count') {
value++;
}
if (shallow === false || depth < shallow) {
propValue = aggregateProperty(propValue, property, aggregate, shallow, depth+1); //recursively aggregate nested objects and arrays
} else {
continue; //skip this property
}
}
//aggregate the properties value based on the selected aggregation method
if ((prop == property || nested) && propValue) {
switch(aggregate) {
case 'sum':
if (!isNaN(propValue)) {
value += propValue;
}
break;
case 'min':
if ((propValue < value) || !value) {
value = propValue;
}
break;
case 'max':
if ((propValue > value) || !value) {
value = propValue;
}
break;
case 'count':
if (propValue) {
if (nested) {
value += propValue;
} else {
value++;
}
}
break;
}
}
}
return value;
}
It is recursive, non ES6, and it should work in most semi-modern browsers. You use it like this:
const onlineCount = aggregateProperty(this.props.contacts, 'online', 'count');
Parameter breakdown:
obj = either an object or an array
property = the property within the nested objects/arrays you wish to perform the aggregate method on
aggregate = the aggregate method (sum, min, max, or count)
shallow = can either be set to true/false or a numeric value
depth = should be left null or undefined (it is used to track the subsequent recursive callbacks)
Shallow can be used to enhance performance if you know that you will not need to search deeply nested data. For instance if you had the following array:
[
{
id: 1,
otherData: { ... },
valueToBeTotaled: ?
},
{
id: 2,
otherData: { ... },
valueToBeTotaled: ?
},
{
id: 3,
otherData: { ... },
valueToBeTotaled: ?
},
...
]
If you wanted to avoid looping through the otherData property since the value you are going to be aggregating is not nested that deeply, you could set shallow to true.
Use Lodash
import _ from 'Lodash';
var object_array = [{a: 1, b: 2, c: 3}, {a: 4, b: 5, c: 6}];
return _.sumBy(object_array, 'c')
// return => 9
I came across this solution from #jbabey while trying to solve a similar problem. With a little modification, I got it right. In my case, the object keys are numbers (489) and strings ("489"). Hence to solve this, each key is parse. The following code works:
var array = {"nR": 22, "nH": 7, "totB": "2761", "nSR": 16, "htRb": "91981"}
var parskey = 0;
for (var key in array) {
parskey = parseInt(array[key]);
sum += parskey;
};
return(sum);
A ramda one liner:
import {
compose,
sum,
values,
} from 'ramda'
export const sumValues = compose(sum, values);
Use:
const summed = sumValues({ 'a': 1 , 'b': 2 , 'c':3 });
We can iterate object using in keyword and can perform any arithmetic operation.
// input
const sample = {
'a': 1,
'b': 2,
'c': 3
};
// var
let sum = 0;
// object iteration
for (key in sample) {
//sum
sum += (+sample[key]);
}
// result
console.log("sum:=>", sum);
A simple solution would be to use the for..in loop to find the sum.
function findSum(obj){
let sum = 0;
for(property in obj){
sum += obj[property];
}
return sum;
}
var sample = { a: 1 , b: 2 , c:3 };
console.log(findSum(sample));
function myFunction(a) { return Object.values(a).reduce((sum, cur) => sum + cur, 0); }
Sum the object key value by parse Integer. Converting string format to integer and summing the values
var obj = {
pay: 22
};
obj.pay;
console.log(obj.pay);
var x = parseInt(obj.pay);
console.log(x + 20);
function totalAmountAdjectives(obj) {
let sum = 0;
for(let el in obj) {
sum += el.length;
}
return sum;
}
console.log(totalAmountAdjectives({ a: "apple" }))
A simple and clean solution for typescrip:
const sample = { a: 1, b: 2, c: 3 };
const totalSample = Object.values(sample).reduce(
(total: number, currentElement: number) => total + currentElement
);
console.log(totalSample);
Good luck!

Recognize the last iteration in a Javascript object

I have an object that I'm iterating
for (el in object) {
// Some work here
}
I want to know when is the last iteration, inside the iteration, so I can do
for (el in object) {
// Some work here
if (last_iteration) {
// Do something
}
}
Any straightforward way to do it?
I know I'm late but I just ran into this and fixed it like this:
let i = 0;
const object = { a: 1, b: 2 };
const length = Object.keys(object).length;
for (el in object) {
const last = i === length - 1; // true if last, false if not last
console.log(i, el, last);
i++;
}
Update: A few years later, i++ at the end of a loop really irks me.
const object = { a: 1, b: 2 };
const length = Object.keys(object).length;
for (const [key, isLast] of Object.keys(object)
.map((key, i) => [key, i === length - 1])) {
console.log(key, isLast);
}
or
const object = { a: 1, b: 2 };
const length = Object.keys(object).length;
Object.keys(object)
.map((key, i) => [key, i === length - 1]))
.map(([key, isLast]) => {
console.log(key, isLast);
})
You can do something like this:
var first = true;
var prev;
for (var el in object) {
// Some work here
if (first) {
first = false;
} else {
doSomething(prev, object[prev]);
}
prev = el;
}
if (prev !== undefined) { // There was at least one element
doSomethingElse(prev, object[prev]); // Prev is now last of all elements
}
This is in case you want to process all but the last element in one way (doSomething) and process the last element in another way (doSomethingElse).
If you want to process all the elements in one way (doSomething) and want to have extra processing for the last element only (doSomethingExtra), you can do:
var prev;
for (var el in object) {
// Some work here
doSomething(el, object[el]);
prev = el;
}
if (prev !== undefined) { // There was at least one element
doSomethingExtra(prev, object[prev]); // Prev is now last of all elements
}
To make it even shorter, you can do similar to what Török Gábor did in the gist he provided, by reusing el variable, i.e.:
var el;
for (el in object) {
// Some work here
doSomething(el, object[el]);
}
if (el !== undefined) { // There was at least one element
doSomethingExtra(el, object[el]); // El is now last of all elements
}
Hope this helps.
If the keys are not numerical, this works:
let anObject = {'one': 1, 'two': 2, 'three': 3, 'lastKey': 4};
let objectKeys = Object.keys(anObject);
let lastObjectKey = objectKeys.slice(-1).toString();
console.log(lastObjectKey); // 'lastKey'
The Object.keys() method returns an array of a given object's own enumerable property names, iterated in the same order that a normal loop would.
Example with numerical keys causing reordering:
let anObject2 = {3: 3, 2: 2, 'notLastKey': 4, 1: 'lastKey'};
let objectKeys2 = Object.keys(anObject2);
console.log(objectKeys2); // ["1", "2", "3", "notLastKey"]
let lastObjectKey2 = objectKeys2.slice(-1).toString();
console.log(lastObjectKey2); // "notLastKey"
Note that this will only work if the object you are iterating over is an array (has numeric keys)
var a = [1,2,3,4,5];
for (i in a) {
if(a[+i+1] === undefined)
console.log('the last one is: ' + a[i]);
}
Note that the + sign before i is necessary since if omitted, it will do a string concatenation, the keys resulting in 01, 12, 23, etc
as said already, there is no distinct order for properties, so last enumerated property is only known afterwards.
var object = { a: 'b', c: 42 };
for ( var string in object ) ;
alert( object[string] ); // last property name is still here

Categories