myArr = ['a', 'b', 'c' ];
myArr.reduce((obj, val) => ({ ...obj, [val]: val }));
Based on my understanding, you would expect the reduce to return { a: 'a', b: 'b', c: 'c' }
What we actually get back is { 0: 'a', b: 'b', c: 'c' }
I tried putting a log inside to see what is going on with that first item, but the output is:
b
c
{0: "a", b: "b", c: "c"}
So now the behaviour is even more strange because we don't get any logs for the first val iteration.
let myArr = ['a', 'b', 'c' ];
let result = myArr.reduce((obj, val) => ({ ...obj, [val]: val }), {});
console.log(result);
You missed the initial value to reduce. When no initial value is supplied, reduce pops off the first element for this purpose (and indeed no iteration happens; because 1+2+3 has two additions, not three, unless you specify we have to start from 0).
The first element is "a", which deceptively becomes the misnamed obj; when you execute {..."a", b: "b"}, you will see that ..."a" expanded in the object context will yield the characters' index as the key; thus, ..."a" is equivalent to ...{0: "a"}.
Good thing you didn't try with myArr = ['hello', 'world'] - that'd be much more of a surprise, I imagine (the result from that being {0: "h", 1: "e", 2: "l", 3: "l", 4: "o", world: "world"}).
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)
This question already has answers here:
How to get a subset of a javascript object's properties
(36 answers)
Closed 2 years ago.
I have a bit JSON object with over 50 keys like
const data = {a:1, b:2, c:3,....,y:99,z:100}
and 2 arrays containing keys of this json object
const arr1 = ["a", "b", "m", "l", "x", "y", "Z"]
const arr2 = ["c", "d", "g", "h", "i", "k", "q"]
Now I want to copy all the value from data object into 2 new objects which contain only data of those keys which are present in arr1 and arr2 into 2 objects
const data = { a: 1, b: 2, c: 3, d: 4, y: 99, z: 100 };
const arr1 = ['a', 'b', 'm', 'l', 'x', 'y', 'Z'];
const arr2 = ['c', 'd', 'g', 'h', 'i', 'k', 'q'];
const res1 = arr1.map(item => ({ [item]: data[item] }));
const res2 = arr2.map(item => ({ [item]: data[item] }));
console.log(res1);
console.log(res2);
Maybe you can try, is very easy and there are a lot of ways to do.
var valuesFromArr1 = arr1.map(key => data[key]);
If you want all your keys in a single object, consider using an Array reduce method. Here I abstracted it into a reusable function and applied it to each of your desired arrays.
const data = {a:1, b:2, c:3, y:99, z:100};
const arr1 = ["a", "b", "c"];
const arr2 = ["c", "y", "z"];
function arrToData(arr, data) {
return arr.reduce((acc, el) => {
acc[el] = data[el];
return acc;
}, {});
}
console.log(
arrToData(arr1, data),
arrToData(arr2, data)
);
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' }
I have an array that looks like:
[
["A","B","C","D"],
["E","F","G","H"],
[6,43,2,4]
]
I want to sort this array in descending order of the items in the third row, such that it looks like:
[
["B","A","D","C"],
["F","E","H","G"],
[43,6,4,2]
]
So far this is what I wrote:
var arr = [
["A","B","C","D"],
["E","F","G","H"],
[6,43,2,4]
]
arr = arr.sort((a,b) => {
return b[2] - a[2]
});
console.log(arr);
But the array does not get sorted. Where did I go wrong?
You could take the indices of the array for sorting, sort this array by the values of the original array and map all reordered items.
var array = [["A", "B", "C", "D"], ["E", "F", "G", "H"], [6, 43, 2, 4]],
indices = [...array[2].keys()].sort((a, b) => array[2][b] - array[2][a]);
array = array.map(a => indices.map(i => a[i]));
console.log(array.map(a => a.join(' ')));
You want to sort columns. It is easier to sort rows. So, you could transpose then sort then retranspose:
const arr = [
["A","B","C","D"],
["E","F","G","H"],
[6,43,2,4]
];
const transpose = array => array[0].map((col, i) => array.map(row => row[i]));
const t = transpose(arr);
t.sort((rowi,rowj) => (rowj[rowj.length - 1] - rowi[rowi.length-1]));
console.log(transpose(t));
Which has the intended output of:
[ [ 'B', 'A', 'D', 'C' ],
[ 'F', 'E', 'H', 'G' ],
[ 43, 6, 4, 2 ] ]
This is clearly not as efficient as the other answers, but the fact that you are asking the question suggests that your current way of storing the data isn't convenient for your purposes. Perhaps you might want to transpose then skip the retranspose part.
You need to first sort the number store indexes and then sort the other arrays.
var arr = [
["A","B","C","D"],
["E","F","G","H"],
[6,43,2,4]
]
var tmp = arr[2].map((value, i) => ({value, i})).sort((a, b) => b.value - a.value);
arr = arr.map(function(arr) {
return tmp.map(({value, i}) => arr[i]);
});
console.log(arr);
It would be easier if you could depict the data differently. The Array.sort function will not help you with your current example.
var arr = [{
a: "A", b: "E", c: 6
},{
a: "B", b: "F", c: 43
},{
a: "C", b: "G", c: 2
},{
a: "D", b: "H", c: 4
}]
arr.sort((a, b) => {
return b.c - a.c;
})
console.log(arr);
If you can't, I could provide a different answer; but the answer would be very inefficient.
With ES7 ES2017 (ES8) I can convert an object to an array of arrays like this:
let obj = { a: '1', b: '2', c: '3' }
let arr = Object.entries(obj)
// [ [ 'a', '1' ], [ 'b', '2' ], [ 'c', '3' ] ]
But what if I want to go the other way around? I could use a for-loop, or the reduce-method of the Array like below, but is there a more direct approach?
let arr =[ [ 'a', '1' ], [ 'b', '2' ], [ 'c', '3' ] ]
let obj = arr.reduce((o, x)=>{o[x[0]]=x[1];return o}, {})
// { a: '1', b: '2', c: '3' }
A use case: I wanted to convert a string like "a=1;b=2;c=3" to an object like { a: '1', b: '2', c: '3' } and ended up with something like
"a=1;b=2;c=3"
.split(";")
.reduce((o, x)=>{
let parts=x.split("=")
o[parts[0]]=parts[1]
return o
}, {});
Edit: As pointed out in comments, the reduce syntax could be simplified (or shortened, at least) to:
arr.reduce((o, [k, v])=>(o[k]=v, o), {})
...or written with Object.assign like this:
arr.reduce((o, [k, v])=>Object.assign(o, {[k]: v}), {})
Either way, the answer to the question, according to the comments, is no. This is the best approach we've got.
Edit, Jan 2023: .fromEntries() is in stage 4 since 2019, and now supported in all modern browsers.
Do you want some one-line unentries function?
const detries = entries => entries.reduce((result, [key, value]) => Object.assign(result, {[key]: value}), {});
const entries = Object.entries({a: 1, b: 2, c: 3});
const detried = detries(entries);
console.info("ENTRIES", entries);
console.info("DETRIED", detried);
Edit: Object.fromEntries proposal.
You will probably be able to use this one instead.
Object.fromEntries is proposed to perform the reverse of Object.entries: it accepts an iterable of key-value pairs and returns a new object whose own keys and corresponding values are given by those pairs.
Object.fromEntries([["apple", 0], ["banana", 1]]) // {apple: 0, banana: 1}