i want to create an object like this using iteratio:
opts = [{label:1, value:1}, {label:4, value:4}]
the values inside this object are inside an array portArray = [1,4]
I'm writing
const portArray = [1,4];
return {
portArray.map(value =>
({ label: value value: value }))
};
});
but it does not seem to work. I'm missing something but i dont know what. Any ideas?
The code you have provided is not valid. It contains an illegal return statement.
You can reach the desired solution with using e.g. Object.assign.
const portArray = [1, 4],
res = portArray.map(v => Object.assign({}, { label: v, value: v }));
console.log(res);
The code you provided is quite confusing, but I think I got the point of your question. Since map returns a new array, assuming you have this array: var portArray = [1,4]; you can use map like this:
function manipulateArray(data) {
return data.map((value) => {
return {label: value, value: value};
}
}
var newArray = manipulateArray(portArray);
This way newArray will equal [{label:1, value:1}, {label:4, value:4}].
You should probably read map' documentation here: MDN
You code is missing comma separating your object properties:
{
label: value, // <-- comma between properties
value: value
}
In addition, Array#map will return a new array of containing your values mapped to objects which you can store in a local variable:
const portArray = [1,4];
const newArray = portArray.map(value =>({ label: value, value: value }));
// remember this comma :) ----------------^
Side note about implicit vs explicit return value for arrow functions:
Parenthesis around the object literal following the arrow function, they are necessary so that function's body expression is returned implicitly.
Use of explicit return statement in arrow function (notice the addition of curly braces around the function body):
const newArray = portArray.map(value => {
return {
label: value,
value: value
}
};
Related
I'm currently learning about the reduce method in JS, and while I have a basic understanding of it, more complex code completely throws me off. I can't seem to wrap my head around how the code is doing what it's doing. Mind you, it's not that the code is wrong, it's that I can't understand it. Here's an example:
const people = [
{ name: "Alice", age: 21 },
{ name: "Max", age: 20 },
{ name: "Jane", age: 20 },
];
function groupBy(objectArray, property) {
return objectArray.reduce((acc, obj) => {
const key = obj[property];
const curGroup = acc[key] ?? [];
return { ...acc, [key]: [...curGroup, obj] };
}, {});
}
const groupedPeople = groupBy(people, "age");
console.log(groupedPeople);
// {
// 20: [
// { name: 'Max', age: 20 },
// { name: 'Jane', age: 20 }
// ],
// 21: [{ name: 'Alice', age: 21 }]
// }
Now the reduce method as I understand it, takes an array, runs some provided function on all the elements of the array in a sequential manner, and adds the result of every iteration to the accumulator. Easy enough. But the code above seems to do something to the accumulator as well and I can't seem to understand it. What does
acc[key] ?? []
do?
Code like this make it seem like a breeze:
const array1 = [1, 2, 3, 4];
// 0 + 1 + 2 + 3 + 4
const initialValue = 0;
const sumWithInitial = array1.reduce(
(accumulator, currentValue) => accumulator + currentValue,
initialValue
);
console.log(sumWithInitial);
// Expected output: 10
But then I see code like in the first block, I'm completely thrown off. Am I just too dumb or is there something I'm missing???
Can someone please take me through each iteration of the code above while explaining how it
does what it does on each turn? Thanks a lot in advance.
You are touching on a big problem with reduce. While it is such a nice function, it often favors code that is hard to read, which is why I often end up using other constructs.
Your function groups a number of objects by a property:
const data = [
{category: 'catA', id: 1},
{category: 'catA', id: 2},
{category: 'catB', id: 3}
]
console.log(groupBy(data, 'category'))
will give you
{
catA: [{category: 'catA', id: 1}, {category: 'catA', id: 2}],
catB: [{category: 'catB', id: 3}]
}
It does that by taking apart the acc object and rebuilding it with the new data in every step:
objectArray.reduce((acc, obj) => {
const key = obj[property]; // get the data value (i.e. 'catA')
const curGroup = acc[key] ?? []; // get collector from acc or new array
// rebuild acc by copying all values, but replace the property stored
// in key with an updated array
return { ...acc, [key]: [...curGroup, obj] };
}, {});
You might want to look at spread operator (...) and coalesce operator (??)
Here is a more readable version:
objectArray.reduce((groups, entry) => {
const groupId = entry[property];
if(!groups[groupId]){
groups[groupId] = [];
}
groups[groupId].push(entry);
return groups;
}, {});
This is a good example where I would favor a good old for:
function groupBy(data, keyProperty){
const groups = {}
for(const entry of data){
const groupId = entry[keyProperty];
if(!groups[groupId]){
groups[groupId] = [];
}
groups[groupId].push(entry);
}
return groups;
}
Pretty much the same number of lines, same level of indentation, easier to read, even slightly faster (or a whole lot, depending on data size, which impacts spread, but not push).
That code is building an object in the accumulator, starting with {} (an empty object). Every property in the object will be a group of elements from the array: The property name is the key of the group, and the property value is an array of the elements in the group.
The code const curGroup = acc[key] ?? []; gets the current array for the group acc[key] or, if there isn't one, gets a new blank array. ?? is the "nullish coalescing operator." It evaluates to its first operand if that value isn't null or undefined, or its second operand if the first was null or undefined.
So far, we know that obj[property] determines the key for the object being visited, curGroup is the current array of values for that key (created as necessary).
Then return { ...acc, [key]: [...curGroup, obj] }; uses spread notation to create a new accumulator object that has all of the properties of the current acc (...acc), and then adds or replaces the property with the name in key with a new array containing any previous values that the accumulator had for that key (curGroup) plus the object being visited (obj), since that object is in the group, since we got key from obj[property].
Here's that again, related to the code via comments. I've split out the part creating a new array [...curGroup, obj] from the part creating a new accumulator object for clarity:
function groupBy(objectArray, property) {
return objectArray.reduce(
(acc, obj) => {
// Get the value for the grouping property from this object
const key = obj[property];
// Get the known values array for that group, if any, or
// a blank array if there's no property with the name in
// `key`.
const curGroup = acc[key] ?? [];
// Create a new array of known values, adding this object
const newGroup = [...curGroup, obj];
// Create and return a new object with the new array, either
// adding a new group for `key` or replacing the one that
// already exists
return { ...acc, [key]: newGroup };
},
/* The starting point, a blank object: */ {}
);
}
It's worth noting that this code is very much written with functional programming in mind. It uses reduce instead of a loop (when not using reduce, FP usually uses recursion rather than loops) and creates new objects and arrays rather than modifying existing ones.
Outside of functional programming, that code would probably be written very differently, but reduce is designed for functional programming, and this is an example of that.
Just FWIW, here's a version not using FP or immutability (more on immutability below):
function groupBy(objectArray, property) {
// Create the object we'll return
const result = {};
// Loop through the objects in the array
for (const obj of objectArray) {
// Get the value for `property` from `obj` as our group key
const key = obj[property];
// Get our existing group array, if we have one
let group = result[key];
if (group) {
// We had one, add this object to it
group.push(obj);
} else {
// We didn't have one, create an array with this object
// in it and store it on our result object
result[key] = [obj];
}
}
return result;
}
In a comment you said:
I understand the spread operator but it's use in this manner with the acc and the [key] is something I'm still confused about.
Yeah, there are a lot of things packed into return { ...acc, [key]: [...curGroup, obj] };. :-) It has both kinds of spread syntax (... isn't an operator, though it's not particularly important) plus computed property name notation ([key]: ____). Let's separate it into two statements to make it easier to discuss:
const updatedGroup = [...curGroup, obj];
return { ...acc, [key]: updatedGroup };
TL;DR - It creates and returns a new accumulator object with the contents of the previous accumulator object plus a new or updated property for the current/updated group.
Here's how that breaks down:
[...curGroup, obj] uses iterable spread. Iterable spread spreads out the contents of an iterable (such as an array) into an array literal or a function call's argument list. In this case, it's spread into an array literal: [...curGroup, obj] says "create a new array ([]) spreading out the contents of the curGroup iterable at the beginning of it (...curGroup) and adding a new element at the end (, obj).
{ ...acc, ____ } uses object property spread. Object property spread spreads out the properties of an object into a new object literal. The expression { ...acc, _____ } says "create a new object ({}) spreading out the properties of acc into it (...acc) and adding or updating a property afterward (the part I've left as just _____ for now)
[key]: updatedGroup (in the object literal) uses computed property name syntax to use the value of a variable as the property name in an object literal's property list. So instead of { example: value }, which creates a property with the actual name example, computed property name syntax puts [] around a variable or other expression and uses the result as the property name. For instance, const obj1 = { example: value }; and const key = "example"; const obj2 = { [key]: value }; both create an object with a propety called example with the value from value. The reduce code is using [key]: updatedGroup] to add or update a property in the new accumulator whose name comes from key and whose value is the new group array.
Why create a new accumulator object (and new group arrays) rather than just updating the one that the code started with? Because the code is written such that it avoids modifying any object (array or accumulator) after creating it. Instead of modifying one, it always creates a new one. Why? It's "immutable programming," writing code that only ever creates new things rather than modifying existing things. There are good reasons for immutable programming in some contexts. It reduces the possibilities of a change in code in one place from having unexpected ramifications elsewhere in the codebase. Sometimes it's necessary, because the original object is immutable (such as one from Mongoose) or must be treated as though it were immutable (such as state objects in React or Vue). In this particular code it's pointless, it's just style. None of these objects is shared anywhere until the process is complete and none of them is actually immutable. The code could just as easily use push to add objects to the group arrays and use acc[key] = updatedGroup; to add/update groups to the accumulator object. But again, while it's pointless in this code, there are good uses for immutable programming. Functional programming usually adheres to immutability (as I understand it; I haven't studied FP deeply).
Current Situation :
[{
"Severity":1,
"Name":"Yash"
}, {
"Severity":2,
"Name":"Yashaswi"
}]
Desired Situation :
[{1: "Yash"}, {2: "Yashaswi"}]
Code being used :
widTags = ["Severity","Name"];
let tempobj = {};
for(let key in widTags) {
tempobj[key]=prop;
}
dataArrayWid.push(tempobj)
This solution does what you're suggesting without changing the syntax too much from your original code:
const original = [
{"Severity":1, "Name":"Yash"},
{"Severity":2, "Name":"Yashaswi"}
];
const final = [];
for (const oldObj of original){ // (Prefer `for...of` to iterate Arrays)
const
newObj = {},
key = oldObj["Severity"],
val = oldObj["Name"];
newObj[key] = val; // Uses Severity val as prop name & Name val as prop val
final.push(newObj);
}
console.log(final);
And this is a more concise version:
const
original = [ {"Severity":1, "Name":"Yash"}, {"Severity":2, "Name":"Yashaswi"} ],
final = original.map(obj => ( { [obj.Severity]: obj.Name } ));
console.log(final);
(Here, the .map method of Arrays makes a new Array with each element modified by a function -- in this case an Arrow function).
Note:
The extra parentheses tell JavaScript that their contents are an expression containing our Object literal to be returned, not a block of code statements.
Similarly, the extra brackets in the Object literal tell JavaScript that their contents are an expression specifying a computed property name, not a static property name,
You can achieve that by using Array.map() method.
Demo :
const dataArrayWid = [{
"Severity":1,
"Name":"Yash"
}, {
"Severity":2,
"Name":"Yashaswi"
}];
const result = dataArrayWid.map((obj) => {
return {
[obj.Severity]: obj.Name
}
});
console.log(result);
I want to build an object using reduce in this way:
const result = [1, 2].reduce((partialResult, actualValue) => {
// define someKey and someValue
partialResult[someKey] = someValue;
return partialResult
}, {});
However, I'm getting the following tslint error:
Modifying properties of existing object not allowed. (no-object-mutation)
How can I solve this?
Either modify the code so that you're not mutating the object, which means you'll have to copy it each time:
const result = [1, 2].reduce((partialResult, actualValue) => {
return {
...partialResult,
[someKey]: someValue,
}
}, {});
Or disable the lint rule (either for this specific line if you like the lint rule in general, or globally if you think it's an unnecessary restriction)
For example:
const result = [1, 2].reduce(
(partialResult, actualValue) => Object.assign({}, partialResult, {[someKey]: someValue]}), {});
You can use map and Object.fromEntries to create the object all-at-once (as far as your code is concerned) rather than mutating or copying the object:
const someKey = "foo";
const result = Object.fromEntries(
[1, 2].map(value => [someKey + value, value])
// ^^^^^^^^^^^^^^^---- see my comment on the question and
// note below
);
console.log(result);
fromEntries creates an object from an array of entries, where each entry is a [key, value] array.
Note that I've used someKey + value as the key because you haven't said how you get the key, and I assume you're not just using the same key every time, since as I commented here, if you were you'd be replacing the property value every time if you did and end up with an object with a single property, making the entire loop pointless.
I'm trying to use the array reduce function to return a 2D array of objects. The input is a comma separated values. The first row of the string is used as the title row. I'm analyzing the solution and I don't understand the notation. Specifically I don't understand the "=> ((obj[title] =
values[index]), obj), {})" portion in the code below. I'm looking to have someone explain it to me. For me it seems like we're initializing obj to be a an object. After that I'm lost.
const CSV_to_JSON = (data, delimiter = ',') => {
const titles = data.slice(0, data.indexOf('\n')).split(delimiter);
return data
.slice(data.indexOf('\n') + 1)
.split('\n')
.map(v => {
const values = v.split(delimiter);
return titles.reduce(
(obj, title, index) => ((obj[title] = values[index]), obj)
, {});
});
};
console.log(CSV_to_JSON('col1,col2\na,b\nc,d')); // [{'col1': 'a', 'col2': 'b'}, {'col1': 'c', 'col2': 'd'}];
console.log(CSV_to_JSON('col1;col2\na;b\nc;d', ';')); // [{'col1': a', 'col2': 'b'}, {'col1': 'c', 'col2': 'd'}]
It's an (ab)use of the comma operator, which takes a list of comma-separated expressions, evaluates the first expression(s), discards them, and then the whole (...) resolves to the value of the final expression. It's usually something only to be done in automatic minification IMO, because the syntax looks confusing.
The .reduce there
return titles.reduce((obj, title, index) => ((obj[title] =
values[index]), obj), {});
is equivalent to
return titles.reduce((obj, title, index) => {
obj[title] = values[index];
return obj;
}, {});
which makes a lot more sense - it turns an array of titles (eg ['foo', 'bar']) and an array of values (eg ['fooVal', 'barVal']), and uses .reduce to transform those into a single object, { foo: 'fooVal', bar: 'barVal' }.
The first argument to the .reduce callback is the accumulator's initial value (the second argument to .reduce), or the value that was returned on the last iteration - the code above passes {} as the initial value, assigns a property to the object, and returns the object on every iteration. .reduce is the usually the most appropriate method to use to turn an array into an object, but if you're more familiar with forEach, the code is equivalent to
const obj = {};
titles.forEach((title, index) => {
obj[title] = values[index];
});
return obj;
While the comma operator can be useful when code-golfing, it's probably not something that should be used when trying to write good, readable code.
I have been trying to figure out how to turn an array into an array with objects.
for example i have a json file to start with and the json file looks sorta like this
var data=[{"tasknumber":304030,
"date":"2012-05-05",
"operator":"john doe"},
{"tasknumber":23130,
"date":"2012-07-07",
"operator":"john doeeeeeeee"},
{"tasknumber":233330,
"date":"2012-08-08",
"operator":"john doe"}]
so i applied the _.countBy function that is within the underscore.js library and i get an object like this
{"john doe":2,"john doeeeeeeee":1}
ive been trying to figure out how to turn this into an array with objects so it would look something like this but i have failed in every attempt and i dont know were to start
[{operator:"john doe",
count: 2},
{operator: "john doeeeeeeee",
count:1}]
i have tried a few things but all i get is tragedy and everything breaks, does anyone know if there are any librarys or anything that could help with this sort of thing?
Given the object (not array) {"john doe":2,"john doeeeeeeee":1} as input you can get your desired output like this:
var input = {"john doe":2,"john doeeeeeeee":1};
var output = Object.keys(input).map(function(k) {
return {
operator: k,
count: input[k]
};
});
console.log(output);
Or with ES6 arrow function syntax:
var input = {"john doe":2,"john doeeeeeeee":1};
var output = Object.keys(input).map((k) => ({ operator: k, count: input[k] }) );
console.log(output);
(Note that Underscore probably provides an even shorter way to do this, but I'm not familiar with Underscore so I've just given a plain JS solution.)
Further reading:
Object.keys()
array .map()
=> arrow functions
Given your initial data array, you can just run this:
var data=[{"tasknumber":304030,
"date":"2012-05-05",
"operator":"john doe"},
{"tasknumber":23130,
"date":"2012-07-07",
"operator":"john doeeeeeeee"},
{"tasknumber":233330,
"date":"2012-08-08",
"operator":"john doe"}];
Function definition
const count = data => {
// get data in format like _.countBy
const o = data.map(x => x.operator).reduce((acc, cur) => { acc[cur] ? acc[cur] += 1 : acc[cur] = 1; return acc; }, {});
// transform object into array of object
return Object.keys(o).map(operator => ({operator, count: o[operator]}));
};
Test it by producing output
console.log(count(data));
Here is an untested underscore approach that takes your initial values as loaded from the JSON file and converts directly into your desired output format:
_.chain(input)
.groupBy(function(entry) { return entry.operator })
.map(function(entries, operator) {
return {
operator: operator,
count: entries.length
}
})
.value();