I wonder if this question has a functional programmiong approach. I have a literal object and some keys:
var obj= {'a':20, 'b':44, 'c':70};
var keys = ['a','c'];
And I want to obtain:
{'a':20', 'c':70}
But without for loop. I tried:
_.object(keys, ._map(keys, function(key){return obj[key];}))
Giving the result but it seems quite complex ..
Since you use underscore.js, try method _.pick(), which was implemented specifically for that:
var obj = {
'a': 20,
'b': 44,
'c': 70
};
var keys = ['a', 'c'];
console.log( _.pick(obj, keys) );
// Object {a: 20, c: 70}
<script src="http://underscorejs.org/underscore-min.js"></script>
You can do it with .reduce():
var extracted = keys.reduce(function(o, k) {
o[k] = obj[k];
return o;
}, {});
The .reduce() method (known as "inject" or "fold" in some other languages) iterates through the values of the array. Each one is passed to the function along with the initial value passed as the second parameter. The function does whatever it needs to do with each array entry and returns the value to be passed on the next iteration.
The pattern above is pretty typical: start with an empty object and add to it with each function call.
Related
I apologize if this is a dumb question. I can't really find any resources via google that go through this topic. I don't understand how to step through an array of properties of an object in a recursion function since by definition a recursion will loop through itself. I know how to iterate through an array without a for loop in a recursion. What I don't understand is how to loop through an object for a Recursion. This is just some code I made up to demonstrate my lack of understanding.
var input1 = [1, 2, 3, 4, 5];
var input2 = {1: 'a', 2: 'b', 3: 'c'};
//for arrays
var arrayRecursion = function(someArray) {
var result = [];
//base case
if (someArray.length === 0) {
return result;
} else {
result.push(someArray.slice(0, 1));
return result.concat(arrayRecursion(someArray.slice(1)));
}
}
//for objects trying to copy input into results
var objectRecursion = function(someObject) {
var result = {};
for (var value in someObject) {
//base case
if (typeof(someObject[key]) !== 'object') {
return result;
}
//recursion
}
}
The main question I have is for my object recursion. If I have an established for - in loop for an object. How does it ever iterate through it? I don't have a recursion filled in because I have no clue how to approach this. If I call the recursion for the object, does it move onto the next property of the object? If so, how? Wouldn't you be starting the for - in loop all over again from the start? I guess where my logic lies is that the for loop is NOT continued from every recursion called because it executes the function which starts the loop from the first property
for..in loops iterate over properties, not values - (var value in someObject) will be quite misleading and result in bugs.
Once you have a reference to a value of the object, check whether it's an object or not. If it's an object, call the recursive objectRecursion and assign the result to the result object at the same property. (Don't return at this point, since that'll terminate the function)
Note that typeof is a keyword, not a function - don't put parentheses after it.
A related issue is that null's typeof is object too, so you'll have to compare against that as well.
var input2 = {1: 'a', 2: 'b', 3: 'c', foo: { prop: 2 }};
const objectRecursion = (someObject) => {
const result = {};
for (const [key, value] of Object.entries(someObject)) {
result[key] = typeof value === 'object' && value !== null
? objectRecursion(value)
: value;
}
return result;
};
console.log(objectRecursion(input2));
For a more flexible function which handles and copies arrays as well:
var input2 = {1: 'a', 2: 'b', 3: 'c', foo: { prop: 2, prop2: [3, 4, 5, { nested: 'nested' }] }};
const objectRecursion = (someItem) => {
if (typeof someItem !== 'object' && someItem !== null) {
return someItem;
}
if (Array.isArray(someItem)) {
return someItem.map(objectRecursion);
}
const result = {};
for (const [key, value] of Object.entries(someItem)) {
result[key] = objectRecursion(value)
}
return result;
};
console.log(objectRecursion(input2));
This should work recursively, using apply
https://jsfiddle.net/cz1frnL8/
var o = {1: 'a', 2: 'b', 3: 'c', foo: { prop: 2, prop2: [3, 4, 5, { nested: 'nested' }] }};
function process(key,value) {
console.log(key + " : "+value);
}
function traverse(o,func) {
for (var i in o) {
func.apply(this,[i,o[i]]);
if (o[i] !== null && typeof(o[i])=="object") {
traverse(o[i],func);
}
}
}
traverse(o,process);
My experience with using recursion with Objects has mainly been with recursing through nested objects rather than through sets of keys and values on the same object. I think this is because recursion as a pattern lends itself naturally to things that are fractal -- that is, where the data being operated on at each level of recursive depth is structurally similar.
Trees are a great example of this. Suppose I have a tree of node objects with the following structure:
4 - 8 - 9
| |
2 5 - 7
|
1
As a JS object, it might look like this.
{
val: 4,
left: {
val: 2,
left: {
val: 1
}
},
right: {
val: 8,
left: {
val: 5,
right: {
val: 7
}
},
right: {
val: 9
}
}
}
Notice how if I were to look at the object representing the left or right node from the root, it's structured the same as its parent? They're each effectively their own tree, but combined into a larger tree (this is what I mean by fractal).
If you wanted to find the largest value in this tree, you could do so by using recursion to iterate through the branches.
const getLargest = function (node) {
return Math.max(node.val, getLargest(node.left), getLargest(node.right));
};
That said, it's totally possible to use recursion on smaller and smaller sets of key-value pairs within an object. It might look something like this:
const exampleObject = {
a: 1,
b: 2,
c: 3
};
const recurse = function(obj) {
const keys = Object.keys(obj);
const firstKey = keys[0];
console.log(obj[firstKey]); // Or whatever; do a thing with the first key-value pair.
const smallerObj = Object.assign({}, obj); // Create a clone of the original; not necessary, but probably a good idea.
delete smallerObj[firstKey]; // Remove the key that we've just used.
recurse(smallerObj);
};
It's a little less natural in JS, but still totally doable. JavaScript object keys aren't sorted, but you could add a sort to const keys = Object.keys(obj) if you wanted to run through the keys in some specific order.
UPDATE:
Many asked why not using [arr[0], arr[1]]. The problem is I have to pass this array to a method, which I don't have access Angular Material Table. And I don't want to call the method over and over again.
I already processed the arr array and I don't want to process pointer array to reflect the new data, which I already know where it is.
The Nina Scholz answer seems to solve the problem.
Is there a way to use "pointers" like C in Javascript?
What I want to do is:
I have an array with objects
const arr = [
{prop: 3},
{prop: 4},
];
And I want to have an array to point to the positions of this array
const pointer = [arr[0], arr[1]]; // I want pointer to point to be an array containing the first and second elements of arr
This will get a reference to the {prop: 3} and {prop: 4} objects, which is not what I want, because, if I do:
arr.splice(0, 0, {prop: 1}); // arr => [{prop:1},{prop:3},{prop:4}]
console.log(pointer); // [{prop: 3},{prop: 4}]
As you can see, pointer holds a reference to the objects {prop:3} and {prop:4}.
How can I achieve pointer to hold reference to the position 0 of the array, instead of the object stored in it? So, on this example, pointer => [{prop:1},{prop:3}]?
I can't call pointer = [arr[0], arr[1]] all the time because arr will change constantly and asynchronously.
Is there a "reactive" way to handle arrays?
If your pointers are always to the same array, you can simply store the indexes.
const pointer = [0, 1];
Then you would use:
console.log(pointer.map(ptr => arr[ptr]));
If your pointers can point to different arrays, you can make the elements of pointer be objects that contain references to the array along with their indexes.
const pointer = [{a: arr, i: 0}, {a: arr1, i: 1}];
console.log(pointer.map(({a, i}) => a[i]));
Interesting aside: several decades ago I used a C implementation for Symbolics Lisp Machines. This is basically how it represented C pointers.
You could use a getter function and return the element of the actual object.
const arr = [{ prop: 3 }, { prop: 4 }];
const pointer = [];
Object.defineProperty(pointer, 0, { get() { return arr[0]; } });
Object.defineProperty(pointer, 1, { get() { return arr[1]; } });
arr.splice(0, 0, { prop: 1 });
console.log(pointer);
You can use a Proxy (not supported by IE) with a get trap:
const arr = [{ prop: 3 }, { prop: 4 }];
const pointer = new Proxy([], {
get(target, prop, receiver) {
// if the prop is a string that can be converted to a number
// return the corresponding value from the arr
if(typeof prop === 'string' && !isNaN(Number(prop))) return arr[target[prop]];
return Reflect.get(target, prop, receiver);
}
});
pointer.push(0, 1);
console.log(pointer);
arr.splice(0, 0, { prop: 1 });
console.log(pointer);
I have
['a', 'b', 'c']
I want to know if this array is contained in this array:
['a', 'b', 'c', 'd']
I know I can do 2 for loops and check item per item, but is there a oneliner for it?
You can do this using Array.prototype.some. This will run the provided function against all the items in an array, and return true if the function returns true for any of them. The following will return true if any items from array are not contained in otherArray, which you can use to determine if one array is fully contained in the other:
return !array.some(function(item) {
return otherArray.indexOf(item) === -1;
});
However, this is not the most elegant solution. The logic can be summed up as:
not any items from array not in other array
Which has far too many negatives. We can instead use Array.prototype.every, which is very similar except it returns true only if all items in an array return true for the provided function. The below is equivalent to what we had before:
return array.every(function(item) {
return otherArray.indexOf(item) !== -1;
});
Except that can be summed up as:
all items in array in other array
Finally, we can implement this as an additional prototype function. Note that the second parameter for every is optional, and sets what this refers to in the function when provided. If we did not pass it in, we would not be able to refer to the this from the outside scope.
Array.prototype.contains = function(array) {
return array.every(function(item) {
return this.indexOf(item) !== -1;
}, this);
}
This can now be used as a one liner in our code:
['a', 'b', 'c'].contains(['a', 'b']) // returns true
If you are able to use ECMAScipt 6, you can use arrow functions to make this a true one-liner.
return array.every(item => otherArray.indexOf(item) !== -1);
ES6 one-lined answer
containedArray.every(element => mainArray.includes(element))
...an improved answer on top of the ES6 suggestion of #James Brierley:
by using every(...) (which returns true if all the elements pass the test we've provided - false otherwise) alongside includes, which IMO is more human-readable - and less error prone - than checking for a index !== -1.
var mainArray = [1, 30, 39, 29, 10, 13];
var containedArray = [1, 30, 39, 29]
console.log(containedArray.every(element => mainArray.includes(element)));
I created this object:
var keys = {A: 'a', B: 'b' };
Later I tried create this other object:
var values = {keys.A: 1, keys.B: 2};
However I got this in Firefox console:
SyntaxError: missing : after property id
Even when I tried:
var vals = {keys['A']: 1, keys['B']: 2}
I got the same error.
The only way to get a success is if I type this:
var vals= {};
vals[keys.A] = 1;
vals[keys.B] = 2;
So, my question is if there is a more elegant way (similar to the first try) to create an anonymous object using as keys the values from a pre-existent object.
Thanks,
Rafael Afonso
Yes, the more elegant way is to use ES6 syntax:
var values = {[keys.A]: 1, [keys.B]: 2};
The thing in brackets can be any expression, so run wild:
var values = { [createPropName() + "_prop"]: 42 }
This is called "computed (dynamic) property names". See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Computed_property_names.
try this
var keys = {A: 'a', B: 'b' },
foo = {};
for (key in keys) { foo[key] = keys[key] }
In an Object Initialiser, the property name must be one of:
IdentifierName
StringLiteral
NumericLiteral
You can do what you want in ES5 using an Object literal for the keys and an Array literal for the values:
var keys = {A: 'a', B: 'b' };
var values = [1, 2];
var obj = {};
Object.keys(keys).forEach(function(v, i) {
obj[v] = values[i];
});
console.log(JSON.stringify(obj)) // {"A":1,"B":2}
However, that isn't reliable because the object properties may not be returned in the order you expect, so you might get:
{"B":1,"A":2};
To do what you want in ES5 and guarantee the order, an array of keys and an array of values is required, so something like:
var keys = ['a', 'b', 'c'];
var values = [1, 2, 3];
var obj = {};
keys.forEach(function(v, i) {obj[v] = values[i]});
console.log(JSON.stringify(obj)) // {"a":1, "b":2, "c":3}
In Javascript I have a JSON object from which I want to process just the items:
var json = {
itema: {stuff: 'stuff'},
itemb: {stuff: 'stuff'},
itemc: {stuff: 'stuff'},
itemd: {stuff: 'stuff'}
}
In Python I could do
print json.items()
[{stuff: 'stuff'},{stuff: 'stuff'},{stuff: 'stuff'},{stuff: 'stuff'}]
Can I do this is js?
You cannot do this the same way as in python without extending Object.prototype, which you don't want to do, because it is the path to misery.
You could create a helper function easily that could loop over the object and put the value into an array however, like this:
function items(obj) {
var i, arr = [];
for(i in obj) {
arr.push(obj[i]);
}
return arr;
}
Ps: JSON is a data format, what you have is an object literal.
In python dict.items returns a list of tuples containing both the keys and the values of the dict. Javascript doesn't have tuples, so it would have to be a nested array.
If you'll excuse me a little python code to show the difference.
>>> {1:2, 2:3}.items()
[(1, 2), (2, 3)]
>>> {1:2, 2:3}.values()
[2, 3]
I see the accepted answer returns an array of the objects values, which is the equivalent of the python function dict.values. What is asked for is dict.items. To do this just loop and build up a nested array of 2 element arrays.
function items(obj){
var ret = [];
for(v in obj){
ret.push(Object.freeze([v, obj[v]]));
}
return Object.freeze(ret);
}
I put the Object.freeze in to be pedantic and enforce that the returned value shouldn't be altered, to emulate the immutability of python tuples. Obviously it still works if you take it out.
It should be noted that doing this somewhat defeats the purpose of items in that it is used when iterating over the object in a linear rather than associative fashion and it avoids calculating the hash value to look up each element in the associative array. For small objects who cares but for large ones it might slow you down and there might be a more idiomatic way to do what you want in javascript.
Another newer way to do it is to use Object.entries() which will do exactly what you want.
Object.entries({1:1, 2:2, 3:3})
.forEach(function(v){
console.log(v)
});
The support is limited to those browser versions mentioned in the documentation.
Thanks to recent updates to JavaScript - we can solve this now:
function items(iterable) {
return {
[Symbol.iterator]: function* () {
for (key in iterable) {
yield [key, iterable[key]];
}
}
};
}
for (const [key, val] of items({"a": 3, "b": 4, "c": 5})) {
console.log(key, val);
}
// a 3
// b 4
// c 5
for (const [key, val] of items(["a", "b", "c"])) {
console.log(key, val);
}
// 0 a
// 1 b
// 2 c
ubershmekel's answer makes use of lazy evaluation, compared to my answer below which uses eager evaluation. Lazy evaluation has many benefits which make it much more appropriate for performance reasons in some cases, but the transparency of eager evaluation can be a development speed boon that may make it preferable in other cases.
const keys = Object.keys;
const values = object =>
keys(object).map(key => object[key]);
const items = object =>
keys(object).map(key => [key, object[key]])
obj = {a: 10, b: 20, c: 30};
keys(obj) // ["a", "b", "c"]
values(obj) // [10, 20, 30]
items(obj) // [["a", 10], ["b", 20], ["c", 30]]
items(obj).forEach(([k, v]) => console.log(k, v))
// a 10
// b 20
// c 30
Not sure what you want to do but I guess Json.stringify will do something like that. See http://www.json.org/js.html