I have the following function:
export const functionName = (key) => {
const data = [
{
displayOrder: 0,
key: 'key-name-1',
},
{
displayOrder: 2,
key: 'key-name-2',
},
];
for (let index in data) {
return data[index].displayOrder === 0
? data[index].key
: data[0].key;
}
};
The purpose of the function is to return the key with the lowest displayOrder. This works, but is there a better more slick method to achieve this?
Secondly, how best could I create a similar version to re-order the entire array based on displayOrder?
The proper method to use when you want to extract a single thing from an array (and you can't identify the element by itself with .find) is to use .reduce:
const functionName = () => {
const data = [
{
displayOrder: 0,
key: 'key-name-1',
},
{
displayOrder: 2,
key: 'key-name-2',
},
];
const lowestDisplayObj = data.reduce((lowestObjSoFar, currentObj) => {
if (currentObj.displayOrder < lowestObjSoFar.displayOrder) return currentObj;
return lowestObjSoFar;
}, { displayOrder: Infinity, key: null });
return lowestDisplayObj.key;
};
console.log(functionName());
Note that your current argument to functionName, key, was unused in your snippet, I'm not sure what it's for.
You could also use .sort() and select the first element in the array, but that's more computationally expensive than it needs to be.
You could make use of array reduce and do something like below.
const data = [{
displayOrder: 10,
key: 'key-name-10',
},
{
displayOrder: 2,
key: 'key-name-2',
},
{
displayOrder: 1,
key: 'key-name-1 Lowest',
}
];
let a = data.reduce((prev, item) => {
if (!prev) {
return item;
}
return (item.displayOrder < prev.displayOrder) ? item : prev;
});
console.log(a.key);
const data = [
{
displayOrder: 0,
key: 'key-name-1',
},
{
displayOrder: 2,
key: 'key-name-2',
},
];
const min = Math.min.apply(null, data.map(({displayOrder}) => +displayOrder));
const obj = data.find((obj) => obj.displayOrder == min);
console.log(obj.key);
Related
I have an array of objects:
[
{ key : '11', value : '1100', $$hashKey : '00X' },
{ key : '22', value : '2200', $$hashKey : '018' }
];
How do I convert it into the following by JavaScript?
{
"11": "1100",
"22": "2200"
}
Tiny ES6 solution can look like:
var arr = [{key:"11", value:"1100"},{key:"22", value:"2200"}];
var object = arr.reduce(
(obj, item) => Object.assign(obj, { [item.key]: item.value }), {});
console.log(object)
Also, if you use object spread, than it can look like:
var object = arr.reduce((obj, item) => ({...obj, [item.key]: item.value}) ,{});
One more solution that is 99% faster is(tested on jsperf):
var object = arr.reduce((obj, item) => (obj[item.key] = item.value, obj) ,{});
Here we benefit from comma operator, it evaluates all expression before comma and returns a last one(after last comma). So we don't copy obj each time, rather assigning new property to it.
This should do it:
var array = [
{ key: 'k1', value: 'v1' },
{ key: 'k2', value: 'v2' },
{ key: 'k3', value: 'v3' }
];
var mapped = array.map(item => ({ [item.key]: item.value }) );
var newObj = Object.assign({}, ...mapped );
console.log(newObj );
One-liner:
var newObj = Object.assign({}, ...(array.map(item => ({ [item.key]: item.value }) )));
You're probably looking for something like this:
// original
var arr = [
{key : '11', value : '1100', $$hashKey : '00X' },
{key : '22', value : '2200', $$hashKey : '018' }
];
//convert
var result = {};
for (var i = 0; i < arr.length; i++) {
result[arr[i].key] = arr[i].value;
}
console.log(result);
I like the functional approach to achieve this task:
var arr = [{ key:"11", value:"1100" }, { key:"22", value:"2200" }];
var result = arr.reduce(function(obj,item){
obj[item.key] = item.value;
return obj;
}, {});
Note: Last {} is the initial obj value for reduce function, if you won't provide the initial value the first arr element will be used (which is probably undesirable).
https://jsfiddle.net/GreQ/2xa078da/
Using Object.fromEntries:
const array = [
{ key: "key1", value: "value1" },
{ key: "key2", value: "value2" },
];
const obj = Object.fromEntries(array.map(item => [item.key, item.value]));
console.log(obj);
A clean way to do this using modern JavaScript is as follows:
const array = [
{ name: "something", value: "something" },
{ name: "somethingElse", value: "something else" },
];
const newObject = Object.assign({}, ...array.map(item => ({ [item.name]: item.value })));
// >> { something: "something", somethingElse: "something else" }
you can merge array of objects in to one object in one line:
const obj = Object.assign({}, ...array);
Use lodash!
const obj = _.keyBy(arrayOfObjects, 'keyName')
Update: The world kept turning. Use a functional approach instead.
Previous answer
Here you go:
var arr = [{ key: "11", value: "1100" }, { key: "22", value: "2200" }];
var result = {};
for (var i=0, len=arr.length; i < len; i++) {
result[arr[i].key] = arr[i].value;
}
console.log(result); // {11: "1000", 22: "2200"}
Simple way using reduce
// Input :
const data = [{key: 'value'}, {otherKey: 'otherValue'}];
data.reduce((prev, curr) => ({...prev, ...curr}) , {});
// Output
{key: 'value', otherKey: 'otherValue'}
More simple Using Object.assign
Object.assign({}, ...array);
Using Underscore.js:
var myArray = [
Object { key="11", value="1100", $$hashKey="00X"},
Object { key="22", value="2200", $$hashKey="018"}
];
var myObj = _.object(_.pluck(myArray, 'key'), _.pluck(myArray, 'value'));
Nearby 2022, I like this approach specially when the array of objects are dynamic which also suggested based on #AdarshMadrecha's test case scenario,
const array = [
{ key : '11', value : '1100', $$hashKey : '00X' },
{ key : '22', value : '2200', $$hashKey : '018' }];
let obj = {};
array.forEach( v => { obj[v.key] = v.value }) //assign to new object
console.log(obj) //{11: '1100', 22: '2200'}
let array = [
{ key: "key1", value: "value1" },
{ key: "key2", value: "value2" },
];
let arr = {};
arr = array.map((event) => ({ ...arr, [event.key]: event.value }));
console.log(arr);
Was did yesterday
// Convert the task data or array to the object for use in the above form
const {clientData} = taskData.reduce((obj, item) => {
// Use the clientData (You can set your own key name) as the key and the
// entire item as the value
obj['clientData'] = item
return obj
}, {});
Here's how to dynamically accept the above as a string and interpolate it into an object:
var stringObject = '[Object { key="11", value="1100", $$hashKey="00X"}, Object { key="22", value="2200", $$hashKey="018"}]';
function interpolateStringObject(stringObject) {
var jsObj = {};
var processedObj = stringObject.split("[Object { ");
processedObj = processedObj[1].split("},");
$.each(processedObj, function (i, v) {
jsObj[v.split("key=")[1].split(",")[0]] = v.split("value=")[1].split(",")[0].replace(/\"/g,'');
});
return jsObj
}
var t = interpolateStringObject(stringObject); //t is the object you want
http://jsfiddle.net/3QKmX/1/
// original
var arr = [{
key: '11',
value: '1100',
$$hashKey: '00X'
},
{
key: '22',
value: '2200',
$$hashKey: '018'
}
];
// My solution
var obj = {};
for (let i = 0; i < arr.length; i++) {
obj[arr[i].key] = arr[i].value;
}
console.log(obj)
You can use the mapKeys lodash function for that. Just one line of code!
Please refer to this complete code sample (copy paste this into repl.it or similar):
import _ from 'lodash';
// or commonjs:
// const _ = require('lodash');
let a = [{ id: 23, title: 'meat' }, { id: 45, title: 'fish' }, { id: 71, title: 'fruit' }]
let b = _.mapKeys(a, 'id');
console.log(b);
// b:
// { '23': { id: 23, title: 'meat' },
// '45': { id: 45, title: 'fish' },
// '71': { id: 71, title: 'fruit' } }
I print a list by iterating the Symbol.iterate object. I want it to resume printing when I add a new item to the array, but it doesn't work. I've tried some ways to do it but I couldn't. How can I overcome this problem?
let arr = [{
name : "Computer",
sold : 1000
},
{
name: "Printer",
sold: 250
},
{
name: "Camera",
sold:290
},
{
name: "Gamepad",
sold: 800
},
{
name: "Keyboard",
sold: 2100
}
]
const iteratorArr = arr[Symbol.iterator]();
const listAll = () => {
var it = iteratorArr.next()
while(!it.done) {
console.log(it.value)
it = iteratorArr.next()
}
}
listAll()
arr.push({name:"Mouse",sold: 2900})
listAll()
Array iterators (and really, any builtin iterators) do not support resumption after having been exhausted once. It is possible to create the iterator before adding values to the array, and still iterate them afterwards, but you must not have run the iterator to completion in between. Example:
const arr = ['a', 'b'];
const iter = arr.values();
console.log(iter.next().value); // not yet done
console.log(iter.next().value); // not yet done
arr.push('c');
console.log(iter.next().value); // not yet done
console.log(iter.next().done); // no value left, closing
arr.push('d');
console.log(iter.next().done); // still closed
To get the behavior you want, you'd need to implement your own iterator:
let arr = [
{ name : "Computer", sold : 1000 },
{ name: "Printer", sold: 250 },
{ name: "Camera", sold:290 },
{ name: "Gamepad", sold: 800 },
{ name: "Keyboard", sold: 2100 }
]
const iteratorArr = {
index: 0,
next() {
const done = this.index >= arr.length
return {done, value: done ? undefined : arr[this.index++]}
},
[Symbol.iterator]() { return this }
}
const listRemaining = () => {
for (const value of iteratorArr) {
console.log(value)
}
console.log(iteratorArr);
}
listRemaining()
arr.push({name:"Mouse",sold: 2900})
listRemaining()
I currently have an array that has the following structure:
data = [
{
time: 100,
info: [{
name: "thing1",
count: 3
}, {
name: "thing2",
count: 2
}, {
}]
},
{
time: 1000,
info: [{
name: "thing1",
count: 7
}, {
name: "thing2",
count: 0
}, {
}]
}
];
But I would like to restructure the array to get something like this:
data = [
{
name: "thing1",
info: [{
time: 100,
count: 3
}, {
time: 1000,
count: 7
}, {
}]
},
{
name: "thing2",
info: [{
time: 100,
count: 2
}, {
time: 1000,
count: 0
}, {
}]
}
];
So basically the key would have to be switched from time to name, but the question is how. From other posts I have gathered that using the map function might work, but since other posts had examples to and from different structures I am still not sure how to use this.
There are a number of ways to achieve this however, the key idea will be to perform a nested looping of both data items and their (nested) info items. Doing that allows your algorithm to "visit" and "map" each piece of input data, to a corresponding value in the resulting array.
One way to express that would be to use nested calls to Array#reduce() to first obtaining a mapping of:
name -> {time,count}
That resulting mapping would then be passed to a call to Object.values() to transform the values of that mapping to the required array.
The inner workings of this mapping process are summarized in the documentation below:
const data=[{time:100,info:[{name:"thing1",count:3},{name:"thing2",count:2},{}]},{time:1e3,info:[{name:"thing1",count:7},{name:"thing2",count:0},{}]}];
const result =
/* Obtain array of values from outerMap reduce result */
Object.values(
/* Iterate array of data items by reduce to obtain mapping of
info.name to { time, count} value type */
data.reduce((outerMap, item) =>
/* Iterate inner info array of current item to compound
mapping of info.name to { time, count} value types */
item.info.reduce((innerMap, infoItem) => {
if(!infoItem.name) {
return innerMap
}
/* Fetch or insert new { name, info } value for result
array */
const nameInfo = innerMap[ infoItem.name ] || {
name : infoItem.name, info : []
};
/* Add { time, count } value to info array of current
{ name, info } item */
nameInfo.info.push({ count : infoItem.count, time : item.time })
/* Compound updated nameInfo into outer mapping */
return { ...innerMap, [ infoItem.name] : nameInfo }
}, outerMap),
{})
)
console.log(result)
Hope that helps!
The approach I would take would be to use an intermediate mapping object and then create the new array from that.
const data = [{time: 100, info: [{name: "thing1", count: 3}, {name: "thing2", count: 2}, {}]}, {time: 1e3, info: [{name: "thing1", count: 7}, {name: "thing2", count: 0}, {}]} ];
const infoByName = {};
// first loop through and add entries based on the name
// in the info list of each data entry. If any info entry
// is empty ignore it
data.forEach(entry => {
if (entry.info) {
entry.info.forEach(info => {
if (info.name !== undefined) {
if (!infoByName[info.name]) {
infoByName[info.name] = [];
}
infoByName[info.name].push({
time: entry.time,
count: info.count
});
}
});
}
});
// Now build the resulting list, where name is entry
// identifier
const keys = Object.keys(infoByName);
const newData = keys.map(key => {
return {
name: key,
info: infoByName[key]
};
})
// newData is the resulting list
console.log(newData);
Well, the other guy posted a much more elegant solution, but I ground this one out, so I figured may as well post it. :)
var data = [
{
time: 100,
info: [{
name: "thing1",
count: 3
}, {
name: "thing2",
count: 2
}, {
}]
},
{
time: 1000,
info: [{
name: "thing1",
count: 7
}, {
name: "thing2",
count: 0
}, {
}]
}
];
var newArr = [];
const objInArray = (o, a) => {
for (var i=0; i < a.length; i += 1) {
if (a[i].name === o)
return true;
}
return false;
}
const getIndex = (o, a) => {
for (var i=0; i < a.length; i += 1) {
if (a[i].name === o) {
return i;
}
}
return false;
}
const getInfoObj = (t, c) => {
let tmpObj = {};
tmpObj.count = c;
tmpObj.time = t;
return tmpObj;
}
for (var i=0; i < data.length; i += 1) {
let t = data[i].time;
for (var p in data[i].info) {
if ("name" in data[i].info[p]) {
if (objInArray(data[i].info[p].name, newArr)) {
let idx = getIndex(data[i].info[p].name, newArr);
let newInfoObj = getInfoObj(t, data[i].info[p].count);
newArr[idx].info.push(newInfoObj);
} else {
let newObj = {};
newObj.name = data[i].info[p].name;
let newInfo = [];
let newInfoObj = getInfoObj(t, data[i].info[p].count);
newInfo.push(newInfoObj);
newObj.info = newInfo;
newArr.push(newObj);
}}
}
}
console.log(newArr);
try to use Object.keys() to get the key
I am using vanilla js for this helper function inside of a react app / typescript.
Json data is fetched this holds every letter of the alphabet which is assigned a value and a key. These are layed out into a grid of tiles. When a user selects a tile, this is added to gameData array in React which is used for a list. If a user clicks onto the same tile this is merged so instead of multiple list elements with same values they are merged with quantity + quantity and value + value
The structure is as such
const apiData = [
{key: 'A', value: 50, quantity: 1, color: '#3498db', ...etc},
{key: 'B', value: 40, quantity: 1, color: '#e67e22', ...etc},
...
]
const gameData = [
{key: 'A', value: 200, quantity: 4, color: '#3498db', ...etc},
{key: 'E', value: 10, color: '#fa0', ...etc},
]
export function groupBy(array: GameData[]) {
const group: GameData[] = Object.values(
array.reduce((acc: any, { value, quantity, ...r }) => {
const key = Object.entries(r).join("-");
acc[key] = acc[key] || { ...r, quantity: 0, value: 0 };
return ((acc[key].value += value), (acc[key].quantity += 1)), acc;
}, {})
);
return group;
}
The reducer works and merges properly but I just feel like there must be a better way to do this. Any ideas?
You can clean your groupBy reduce function as follows:
function groupBy(data: GameData[]) {
const result = data.reduce((total, item) => {
const { key, value, quantity } = item;
const prevItem = total[key] || {};
const {value: prevValue = 0, quantity: prevQuantity = 0} = prevItem;
total[key]= {
...item,
value: prevValue + value,
quantity: prevQuantity + quantity,
};
return total;
}, {});
return Object.values(result);
}
For the given input it produces the folowing result:
const gameData = [
{"key":"A","value":50,"quantity":1,"color":"#3498db"},
{"key":"A","value":50,"quantity":1,"color":"#3498db"},
{"key":"A","value":50,"quantity":1,"color":"#3498db"},
{"key":"B","value":50,"quantity":1,"color":"#3498db"},
{"key":"B","value":40,"quantity":1,"color":"#e67e22"}
];
const result = groupBy(gameData);
/*
result = [
{"key":"A","value":150,"quantity":3,"color":"#3498db"},
{"key":"B","value":90,"quantity":2,"color":"#e67e22"}
]
*/
Your solution seems ok, just a little hard to read. Here are my suggestions.
function groupBy(array){
return Object.values(array.reduce((grouped, {value, quantity,...rest}) => {
const key = Object.entries(rest).join('-');
if(!grouped[key]){
grouped[key] = {...rest, value: 0, quantity: 0};
}
grouped[key].quantity++;
grouped[key].value += value;
return grouped;
},{}));
}
Or using a simple for:
function groupBy(array) {
const grouped = {};
for(let obj of array){
const {value, quantity, ...rest} = obj;
const key = Object.entries(rest).join('-');
if(!grouped[key]){
grouped[key]= {...rest, value: 0, quantity: 0};
}
grouped[key].quantity++;
grouped[key].value+=value;
}
return Object.values(grouped);
}
I have a few questions in regards to what would be the best approach to do the following:
Call two different API:
axios.get(contents);
axios.get(favorites);
Response will Look like this:
contents: [
{
id: 1,
value: someValue
},
{
id: 2,
value: someValue
}
];
favorites: [
{
id: 1,
contentId: 2
}
];
What would be the best approach to loop through each favorite and add an element to the contens array such as isFavorite: true when the contentId matches the id. It should look as follows:
contents: [
{
id: 1,
value: someValue
{,
{
id: 2,
value: someValue
isFavorite: true
{
];
What would be the best place to do this and is there any ES6 syntax that can easily do this? I currently have the two actions separate, one that gets the contents and one that gets the favorites, I could possibly merge those or combine them at the reducer.
Any suggestions?
You can use a Set to collect all contentId values from favorites and then iterate through your contents array. This has better time complexity than using some on an array because calling .has() on a Set is O(1):
let contents = [{
id: 1,
value: 'someValue1'
},
{
id: 2,
value: 'someValue2'
},
{
id: 3,
value: 'someValue'
}
];
let favorites = [{
id: 1,
contentId: 2
},
{
id: 2,
contentId: 3
}
];
let favoriteContents = new Set(favorites.map(f => f.contentId));
contents.forEach(c => {
if (favoriteContents.has(c.id)) c.isFavorite = true;
});
console.log(contents);
const newContents = contents.map((content) => {
const foundFavorite = favorites.find((favorite) => favorite.contentId === content.id)
if (foundFavorite) {
return {
...content,
isFavorite: true,
}
}
return content
});
You firstly need to have the promises from your API calls, and when both of them are complete you can then carry out the merge of the results.
const contentsApi = () => Promise.resolve([
{
id: 1,
value: 'foo'
},
{
id: 2,
value: 'bar'
}
])
const favouritesApi = () => Promise.resolve([
{
id: 1,
contentId: 2
}
])
let contents;
let favourites;
const contentsApiCall = contentsApi().then(res => {
contents = res;
})
const favouritesApiCall = favouritesApi().then(res => {
favourites = res;
})
Promise.all([contentsApiCall, favouritesApiCall]).then(() => {
const merged = contents.map(content => {
if(favourites.some(favourite => favourite.contentId === content.id)){
return {
...content,
isFavourite: true
}
} else {
return content;
}
})
console.log(merged)
// do whatever you need to do with your result, either return it if you want to chain promises, or set it in a variable, etc.
})