Get the key of an associative array - javascript

I'm currently fetching an array with .each:
$.each(messages, function(key,message){ doStuff(); });
But the key is the index of the array and not the associative key.
How can I get it easily?

JavaScript doesn't have "associative arrays". It has arrays:
[1, 2, 3, 4, 5]
And objects:
{a: 1, b: 2, c: 3, d: 4, e: 5}
Array's don't have "keys". They have indices, which are counted starting at 0.
Arrays are accessed using [], and objects can be accessed using [] or ..
Example:
var array = [1,2,3];
array[1] = 4;
console.log(array); // [1,4,3]
var obj = {};
obj.test = 16;
obj['123'] = 24;
console.log(obj); // {test: 16, 123: 24}
If you try to access an array using a string as a key instead of an int, that may cause problems. You would be setting a property of the array and not a value.
var array = [1,2,3];
array['test'] = 4; // This doesn't set a value in the array
console.log(array); // [1,2,3]
console.log(array.test); // 4
jQuery's $.each works with both of these. In the callback for $.each, the first parameter, key, is either the object's key, or the array's index.
$.each([1, 2, 3, 4, 5], function(key, value){
console.log(key); // Logs 0 1 2 3 4
});
$.each({a: 1, b: 2, c: 3, d: 4, e: 5}, function(key, value){
console.log(key); // Logs 'a' 'b' 'c' 'd' 'e'
});

var data = {
val1 : 'text1',
val2 : 'text2',
val3 : 'text3'
};
$.each(data, function(key, value) {
alert( "The key is '" + key + "' and the value is '" + value + "'" );
});
​
See the Demo

JavaScript doesn't have "associative arrays" as in PHP, but objects. Objects, though, may have string keys that corresponds to a value. Arrays are lists of values indexed numerically, so, if key is a number, it must be an array you are working with and not an object, and therefore you cannot get the key, as there is none.
Thus, you'd probably want to iterate over an array with a simple for-loop instead of a callback-based iterator such as $.each.

Related

Javascript Object.keys issue

When I run the following code:
var object1 = {
a: 1,
b: 2
};
var object2 = {
b: 4,
c: 3
};
var obj1Keys = Object.keys(object1);
var obj2Keys = Object.keys(object2);
console.log(obj2Keys[0] in obj1Keys);
The console logs false. Clearly both object1 and object2 have property 'b'. What am I doing wrong?
You need to use the includes() method instead.
console.log(obj1Keys.includes(obj2Keys[0]));
This is because the in operator works on object keys. The "keys" to an array are number indices (0, 1, etc), not their values
You need to take the object, not the array with the keys for checking and in operator. This check the keys of the object and arrays have indices and length property.
If you like to take the array with the keys, cou could use Array#includes.
var object1 = { a: 1, b: 2 },
object2 = { b: 4, c: 3 },
obj1Keys = Object.keys(object1),
obj2Keys = Object.keys(object2);
console.log(obj2Keys[0] in object1);
console.log(obj1Keys.includes(obj2Keys[0]));
By asking if obj2Keys[0] in obj1Keys, you're asking whether 'b' is a key of ['a', 'b']. It's not. It's a value of ['a', 'b']. You could check like this instead:
obj1Keys.includes(objKeys[0])

Grouping array of objects and doing a calculation to create a new common value

I'm using lodash to group my objects in my array.
var grouped = _.groupBy(this.tableData, function(obj) {
return obj.key
});
It's giving me an object with key value pairs.
{mykey1: [{obj1}, {obj2}], mykey2: [obj1]}
My question is how can I loop through the objects inside each key and do a calculation. for example if I have an attribute in my object, cost, I want to sum up all the cost inside each object in my key?
You can use a combination of _.mapValues and _.sumBy:
var summed = _.mapValues(grouped, function(v){
return _.sumBy(v, 'cost');
});
Live example below:
var tableData = [
{key:"obj1",cost:1},
{key:"obj1",cost:1},
{key:"obj1",cost:1},
{key:"obj2",cost:1},
{key:"obj2",cost:1}
]
var grouped = _.groupBy(tableData, function(obj) {
return obj.key
});
var summed = _.mapValues(grouped, function(v){
return _.sumBy(v, 'cost');
});
console.log(summed);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
You could do native javascript:
//let obj = {key1: [{cost: 1}, {cost: 2}], key2: [{cost: 3}]}; example input
Object.values(obj).reduce((sum, arr)=> sum + arr.reduce((subsum, element)=>subsum + element.cost, 0), 0);
Explain:
Object.values(obj) //-> [[{cost: 1}, {cost: 2}], [{cost: 3}]]
[1, 2, 3].reduce((sum, element)=> sum + element), 0) //-> reduce (converting a list of things into one)

Adding array to numbers from array [duplicate]

According to the MDN JavaScript documentation you can define object literal property names using integers:
Additionally, you can use a numeric or string literal for the name of a property.
Like so:
me = {
name: "Robert Rocha",
123: 26,
origin: "Mexico"
}
My question is, how do you reference the property that has an integer as a name? I tried the usual me.123 but got an error. The only workaround that I can think of is using a for-in loop. Any suggestions?
You can reference the object's properties as you would an array and use either me[123] or me["123"]
Dot notation only works with property names that are valid identifiers. An identifier must start with a letter, $, _ or unicode escape sequence. For all other property names, you must use bracket notation.
In an object literal, the property name must be an identifier name, string literal or numeric literal (which will be converted to a string since property names must be strings):
var obj = {1:1, foo:'foo', '+=+':'+=+'};
alert(obj[1] + ' ' + obj.foo + ' ' + obj['+=+']); // 1 foo +=+
You can use me[123] or me["123"]. Both work.
You can use bracket notation me[123].
Just in case anyone else was confused by this: using integer (rather than string) property names may give slightly different - though functionally the same - results (depending on the browser) when you have objects within objects.
Simple objects with no nested objects have consistent behavior across browsers (though as the accepted answer says, we need to use brackets instead of dots to access integer property names):
var str_simple = {
a: "b", c: "d", e: "f", g: "h",
};
str_simple.a === "b"; // true
str_simple.e === "f"; // true
var int_simple = {
1: 2, 3: 4, 5: 6, 7: 8,
};
int_simple[1] === 2; // true - must use brackets instead of dots
int_simple[5] === 6; // true
// this works b/c int property names are coerced to strings anyway
int_simple[1] === int_simple['1']; // true
And this nested object with string keys works exactly as expected:
var str_nested = {
a: {b: "c"},
d: {e: "f", g: "h"},
};
str_nested.a; // returns object as expected, no matter the browser - {b: "c"}
str_nested.a.b === "c"; // true
str_nested.d.g === "h"; // true
But this equivalent nested object with integer keys returns slightly different results depending on the browser, though you can still access the nested objects in the same way (so functionally, it still works the same):
var int_nested = {
1: {2: 3},
4: {5: 6, 7: 8},
};
// latest Chrome (57)
// Safari 10 (latest for my Mac, 10.10 Yosemite)
int_nested[1]; // returns object as expected - {2: 3}
int_nested[1][2] === 3; // true
// latest Firefox (52)
int_nested[1]; // RETURNS ARRAY-LIKE OBJECT - Object [ <2 empty slots>, 3 ]
int_nested.length; // undefined because it's not technically an array
int_nested[1][2] === 3; // true - works b/c object was padded with empty slots
// and again, in all browsers, we can exchange the integer keys
// for equivalent strings since property names are coerced to strings anyway
int_nested[1][2] === int_nested['1'][2];
int_nested['1'][2] === int_nested[1]['2'];
int_nested[1]['2'] === int_nested['1']['2'];
This behavior will still be slightly different but functionally the same if you programmatically construct a nested object. For example, say we wanted to write a function that would take a list of pairs (e.g. [[0, 0], [0, 1], [1, 2], [2, 3]]) and convert it into a nested object so we could check if the pair is in the object with O(1) time (e.g. {0: {0: true, 1: true}, 1: {2: true}, 2: {3, true}}). Note that Sets check reference equality and not value equality, so we couldn't store the pair itself in the Set and achieve the same results:
// [[0, 0], [0, 1], [1, 2], [2, 3]] ->
// {
// 0: {0: true, 1: true},
// 1: {2: true},
// 2: {3: true},
// }
function createNestedObject(pairs) {
var obj = {};
for (var pair of pairs) {
var x = pair[0], y = pair[1];
// must create outer object for each unique x or else
// obj[x][y] would fail b/c obj[x] would be undefined
if (!obj.hasOwnProperty(x)) {
obj[x] = {};
}
obj[x][y] = true;
}
return obj;
}
function exists(nested, pair) {
var x = pair[0], y = pair[1];
// uses !! operator so if pair isn't in nested
// we return false instead of undefined
return !!(nested[x] && nested[x][y]);
}
Pairs with strings will work as expected:
var pairs = [["a", "a"], ["a", "b"], ["c", "d"], ["d", "e"]];
var nested = createNestedObject(pairs);
nested; // as expected - {a: {a: true, b: true}, c: {d: true}, d: {e: true}}
exists(nested, ["a", "a"]); // true
exists(nested, ["a", "b"]); // true
exists(nested, ["ZZZ", "ZZZ"]); // false
But in certain browsers, integer pairs will be different but functionally the same:
var pairs = [[0, 0], [0, 1], [1, 2], [2, 3]];
var nested = createNestedObject(pairs);
nested; // in Safari 10/Chrome 57 - returns nested objects as expected
nested; // in Firefox 52 - Object [ Object[2], Object[3], Object[4] ]
// BUT still gives correct results no matter the browser
exists(nested, [0, 0]); // true
exists(nested, [0, 1]); // true
exists(nested, ['0', '0']); // true
exists(nested, [999, 999]); // false
The situation with numeric property names seems more complicated than it is explained in the answers so far. It is true that you can access such properties via for-in loop. However, it might be important to know that for-in loop gives keys as strings, not as numbers as you might expect:
var obj = {1:2};
for (var key in obj) {
alert(typeof(obj[key])); // you get "number" as expected, however
alert(typeof(key)); // you get "string", not "number"
}
A similar thing happens during serialization with JSON:
JSON.stringify( {1:2} ) === '{"1":2}'
So if you code depends on this little detail you better be aware of it.

Convert javascript object to array of individual objects

I have the following object:
{English: 4, Math: 5, CompSci: 6}
How can I convert it to an array of objects, like:
[{English: 4}, {Math: 5}, {CompSci: 6}]
Can't find the answer anywhere. Thanks!!
Use Array#forEach over Object.keys(YOUR_OBJECT)
var input = {
English: 4,
Math: 5,
CompSci: 6
};
var op = [];
Object.keys(input).forEach(function(key) {
var obj = {};
obj[key] = input[key];
op.push(obj); //push newly created object in `op`array
});
console.log(op);
With newer JS, you could take Object.entries and map single properties.
var object = { English: 4, Math: 5, CompSci: 6 },
array = Object.entries(object).map(([k, v]) => ({ [k]: v }));
console.log(array);
Just loop over each of the keys in the object.
var oldObject = {English: 4, Math: 5, CompSci: 6};
var newArray = [];
// Loop over each key in the object
for (var key in oldObject) {
// Create a temp object
var temp = {};
// Set the key of temp
temp[key] = oldObject[key]
// Push it to the array
newArray.push(temp);
}
console.log(newArray)
you can directly assign object by {} but you must use [] quote for key value if not that will not worked
var obj = {English: 4, Math: 5, CompSci: 6};
var n_obj = [];
for(var i in obj){
n_obj.push({[i]:obj[i]});
}
console.log(n_obj);
You can turn the object into an array of key-value pairs using Object.entries and then map this array to smaller object created using Object.fromEntries from each individual key-value pair (the key part here is the wrapping in another array before passing to fromEntries):
Object.entries(obj).map(e => Object.fromEntries([e]))
The reverse way is similar: We create a big object using Object.fromEntries, and we pass in an array of key-value pairs. This array is created by flat-mapping (i.e. eliminating on extra layer of arrays) the array of objects to an array of key-value pairs we get from calling Object.entries on each small object. The key here is the flat-mapping, without it we would get an array of arrays of key-value pairs because we added that extra layer in the other conversion to separate the properties.
Object.fromEntries(arr.flatMap(o => Object.entries(o)))
You can use JSON.stringify(), String.prototype.match() with RegExp /".*"\:.*(?=,|})/, String.prototype.split() with RegExp /,/, Array.prototype.join() with parameter "},{", JSON.parse()
var obj = {English: 4, Math: 5, CompSci: 6};
var res = JSON.parse("[{"
+ JSON.stringify(obj)
.match(/".*"\:.*(?=,|})/g)[0]
.split(/,/)
.join("},{")
+ "}]");
console.log(res);

Javascript subarray that contains references to another array

How to I achieve the following functionality?
I have an array:
a = [1, 2, 3, 4, 5]
b = [a[1], a[2], a[3]] //This array should be some kind of "array of references"
Any change in the array b should be applied to array a, as well.
The problem is that primitive values (String, Number, Boolean, undefined and null), work by value, and they are non-mutable.
If you use objects as the array elements you can get the desired behavior:
var a = [{value: 1}, {value:2}, {value:3}, {num:4}];
var b = [a[1], a[2], a[3]];
alert(a[1].value); // 2
b[0].value = "foo";
alert(a[1].value); // "foo"

Categories