I noticed a weird behavior of the spreading operator. I want to remap each element of a plain array in a pair key-value (an Object). If the element in the array is a, I want to map it as {a:"a"}.
Spreading the result of map results in a weird object with numeric indexes in the first example, while Object.assign() behaves as expected.
var a = ["a","b","c"]
{...a.map( el => ( {[el]: el}))}
>> {0: {a: "a"}, 1: {b: "b"}, 2: {c: "c"}}
Object.assign({},...a.map( el => ( {[el]: el})))
>> {a: "a", b: "b", c: "c"}
What's the problem here? (tried in Chrome 79)
As some of the comments in the question mention, in the first case, you are destructuring an array inside brackets, which is interpreted as creating an object with the index
a.map(el => ({[el]: el})); // [{a: "a"}, {b: "b"}, {c: "c"}]
{...a.map(el => ({[el]: el}))}; // {"0": { a: "a" }, "1": {b: "b"}, "2": {c: "c"}}
In the second case, you are merging the properties of each element in the array with the empty object passed to Object.assign
Object.assign({}, ...[{a: "a"}, {b: "b"}, {c: "c"}]) // is equivalent to
Object.assign({}, {a: "a"}, {b: "b"}, {c: "c"});
See MDN Object.assign for more details.
const a = { a: 'a' };
const b = { b: 'b' };
console.log(
"These are the same:",
{ ...[a, b] },
{ 0: a, 1: b },
);
console.log(
"These are the same:",
Object.assign(...[a, b]),
Object.assign(a, b),
{ ...a, ...b },
);
console.log(
"These are NOT the same:",
{ ...[a, b] },
{ ...a, ...b },
);
That's because {...obj} is just a copy of the object with "own" and "enumerable" properties. Because a.map( el => ( {[el]: el})) is an array, and that's why the final result is also array-like.
As stated in the other answer, your Object.assign statement is equivalent to
Object.assign({}, {a: "a"}, {b: "b"}, {c: "c"});
so that's why it succeeded.
You also can do:
(inside of NodeJS)
> a.reduce((acc, e) => { acc[e] = e; return acc; }, {})
{ a: 'a', b: 'b', c: 'c' }
> a.reduce((acc, e) => (acc[e] = e, acc), {})
{ a: 'a', b: 'b', c: 'c' }
Related
I'm trying to filter an array and remove the entries that contain certain properties. For example, my array looks like this:
const items = [{a: "apple", b: "banana", c: "coconut"}, {a: "apple"}, {b: "banana, c: "coconut"}];
And I want to filter out the items that have the properties b or c.
Right now I have:
items.filter(item => "b" in item || "c" in item);
but I feel like there is a better way to do this, and set it up so a lot more properties could easily be added in the future.
Put the property names in an array, and use .some() to test if any of those properties exists.
const items = [{
a: "apple",
b: "banana",
c: "coconut"
}, {
a: "apple"
}, {
b: "banana",
c: "coconut"
}];
let properties_to_filter = ["b", "c"]
let result = items.filter(item => properties_to_filter.some(prop => prop in item));
console.log(result);
const items = [{a: "apple", b: "banana", c: "coconut"}, {a: "apple"}, {b: "banana", c: "coconut"}]
let filtredKey = ["a"]
let result = []
items.map(el => {
if(el.hasOwnProperty("c"))
result.push(el)
})
console.log(result)
What ONELINER could I use to join ANY number of objects inside of an array? The repeated values should be placed inside a nested array, like so:
I have this input:
[
{a: 123},
{b: "abc", c: 455},
{d: null, c: 01, b: {}}
]
I should get this result:
{
a: 123,
b: ["abc", {}],
c: [455, 01],
d: null
}
I have tried this, but no results :(
my_array.map(function(x) {
var result = {};
for(var i in x) {
result[i] = x[i];
}
return result;
});
Thank you!
You could reduce the array of objects. Inside, loop through keys of the each object. If the accumulator doesn't have the key, add it. If the key already exists, use concat to create an array of values. [].concat(acc[k], v) will handle if acc[k] is an array or a single value.
const input = [
{a: 123},
{b: 2222, c: 455},
{d: null, c: 01}
]
const output = input.reduce((acc, o) => {
Object.entries(o).forEach(([k, v]) => {
if (acc.hasOwnProperty(k))
acc[k] = [].concat(acc[k], v)
else
acc[k] = v
})
return acc
}, {})
console.log(output)
Here's a ES5 version of the above answer:
var input = [
{a: 123},
{b: 2222, c: 455},
{d: null, c: 01}
]
var output = input.reduce(function(acc, o) {
Object.keys(o).forEach(function(k) {
if (acc.hasOwnProperty(k))
acc[k] = [].concat(acc[k], o[k])
else
acc[k] = o[k]
})
return acc
}, {})
console.log(output)
I want to write a helper function to unpack a specific object property from each object in an array of objects. Sometimes this property will be top level, other times it will be nested an arbitrary number of levels. So the crux of this question is: how can I access an object property based on an array of key names of variable length?
I'm hoping for something like:
const func = (arrOfObjects, ...keys) {
return arrOfObjects.map(object => {
return object[keys[0]][keys[1]] ... [keys[N]];
})
}
with example behaviour:
const input = [
{a: b: {c: 10}},
{a: b: {c: 11}},
{a: b: {c: 12}}
]
console.log(func(input, 'a', 'b', 'c'))
// [10, 11, 12]
console.log(func(input, 'a', 'b'))
// [{c: 10}, {c: 11}, {c : 12}]
I feel like there has to be a nice ES6 wizardry solution but as yet haven't found it so any help would be much appreciated!
Cheers,
P
You can get a short and easy solution using Array#reduce
const input = [
{a: { b: {c: 10}}},
{a: { b: {c: 11}}},
{a: { b: {c: 12}}}
]
console.log(func(input, ['a', 'b', 'c']))
// [10, 11, 12]
console.log(func(input, ['a', 'b']))
// [{c: 10}, {c: 11}, {c : 12}]
function func(input, props) {
return input.map(x => exctractByProps(x, props));
}
function exctractByProps(obj, props) {
return props.reduce(
(acc, prop) => typeof acc === 'object' && prop in acc ? acc[prop] : undefined,
obj
)
}
The main logic is to grab all the properties passed in and then try to get the value corresponding to obj[prop[0]][prop[1]][prop[2]]/* ... */[prop[n]]. If the object has an odd shape that doesn't match up with prop (for example, an input of {a: 1}, ['a', 'b'] or {d: {c: 1}}, ['a', 'b']) then the function returns undefined.
Based on the answers you gave me to my questions and your example. It seems as if the order will of the input will always match the objects nesting. So here is my solution:
const func = (arrOfObjects, ...keys) => {
return arrOfObjects.map(object => {
let obj = object, integer = keys.length;
for (let index = 0; index < integer; index++) {
obj = obj[keys[index]];
if(obj === undefined) break;
}
return obj;
});
};
const input = [
{ a: { b: { c: 10 } } },
{ a: { b: { c: 11 } } },
{ a: { b: { c: 12 } } }
];
console.log(func(input, "a", "b", "c"));
// [10, 11, 12]
console.log(func(input, "a", "b"));
// [{c: 10}, {c: 11}, {c : 12}]
Unfortunately there is no such thing as the javascript magic you where expecting.
Note: this code will not work when the order of the keys inside the object are nested at random depth. But for what you are trying to solve, this should work just fine. Also, I tried to preserve your initial code as good as possible
If you supply the accessor like [a, b, c[0], name], you can write a custom function which returns the value if found in a nested Object, otherwise returns undefined
let obj = {
a: 1,
b: 2,
c: {
d: 1,
e: [{
f: 3,
g: [{
z: 45
}]
}]
}
}
function findKeyFromPattern(obj, patternArr) {
let value = obj;
for(let i = 0; i < patternArr.length; i++) {
const arrmatch = patternArr[i].match(/(\w+)\[(\d+)\]/);
if(arrmatch) { // pattern matches and array accessor syntax
value = value[arrmatch[1]];
if(typeof value === 'object' && value !== null) {
value = value[arrmatch[2]];
} else {
return;
}
} else {
if(value[patternArr[i]]) {
value = value[patternArr[i]];
} else {
return;
}
}
}
return value;
}
console.log(findKeyFromPattern(obj, ['c', 'e[0]', 'g']));
console.log(findKeyFromPattern(obj, ['c', 'e[1]', 'g']))
Using the built in lodash functions, is it possible to pass in an object and a list of keys and have the key/value pairs be removed from the object and put into a new object which is returned. An example:
var obj = {a: 1, b: 2, c: 3};
pluck(obj, ['a', 'b']) // returns {a: 1, b: 2}
console.log(obj) // {c: 3}
where pluck is replaced by one or more lodash functions.
Yes using omit and pick:
obj = _.pick(obj, ['a', 'b']); // { a: 1, b: 2 }
For the new object:
var newobj = _.omit(obj, ['a', 'b']); // { c: 3 }
I don't think lodash has anything built in for that. You can do it easily enough just with JavaScript native features, though:
var obj = {a: 1, b: 2, c: 3};
console.log(['a', 'b'].reduce(function(newObj, name) {
newObj[name] = obj[name];
delete obj[name];
return newObj;
}, {})); // {a: 1, b: 2}
console.log(obj) // {c: 3}
For something more lodash-y, you could combine _.pick and _.omit, but you'd be creating a new object rather than deleting properties from an existing one (which might be better anyway):
var obj = {a: 1, b: 2, c: 3};
console.log(_.pick(obj, ['a', 'b'])); // {a: 1, b: 2}
obj = _.omit(obj, ['a', 'b']);
console.log(obj) // {c: 3}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
99.99999% of the time it doesn't matter, but just as an FYI, when you use delete on an object, on some JavaScript engines that puts the object into an unoptimized "dictionary" mode where subsequent property lookup is much slower than it was in the optimized object. (Adding properties to objects doesn't do this, just deleting them.) Again, usually it doesn't matter, but...
I'm new to using Ramda.js and am wondering how I can filter an object based on specified properties.
Looking at R.filter, it seems that _.filter only passes the object value and not the property. For instance, the example given in the REPL:
var isEven = (n, prop) => {
console.log(typeof prop);
// =>
// undefined
// undefined
// undefined
// undefined
return n % 2 === 0;
}
R.filter(isEven, {a: 1, b: 2, c: 3, d: 4}); //=> {b: 2, d: 4}
If I have the following object:
const obj = {a: 1, b: 2, c: 3};
My desired result would be:
const filterProp = (x) => /* some filter fn */;
filterProp('b')(obj);
// => {a: 1, c: 3};
How can I use Ramda to filter the properties of an object?
After digging through the Ramda docs, I found R.omit which satisfies my particular use case.
const obj = {a: 1, b: 2, c: 3};
R.omit(['b'], obj);
// => {a: 1, c: 3};
Use the pickBy method which allows you to filter a collection based on the keys.
const obj = {a: 1, b: 2, c: 3};
var predicate = (val, key) => key !== 'b';
R.pickBy(predicate, obj);
// => {a: 1, c: 3}