Dynamic keys and properties in object [duplicate] - javascript

Suppose I have an array of object:
const apple = [{"bookName" :'Harry Pottar',part:"1"},{"bookName" :'Harry Pottar',part:"2"},
{"bookName": 'LOTR',part:"1"},{"bookName": 'LOTR',part:"2"},{"bookName": 'LOTR',part:"3"}]
I want to get count of all common values along with the value name as :
Expected O/P : [{"Harry Pottar":2},{"LOTR":3"}]
For this I tried as:
const id = "Harry Pottar";
const count = array.reduce((acc, cur) => cur.bookName === id ? ++acc : acc, 0);
As this gives the count, by this I can get count for each bookName. But how can I achieve my expected O/P scenario.
If anyone needs any further information please do let me know.

Good to see you know about .reduce! You’re pretty close, just need to save the result to a hashmap (plain object in JS).
const array = [{"bookName" :'Harry Pottar',part:"1"},{"bookName" :'Harry Pottar',part:"2"},{"bookName": 'LOTR',part:"1"},{"bookName": 'LOTR',part:"2"},{"bookName": 'LOTR',part:"3"}]
const result = array.reduce((acc, item) => {
const key = item.bookName
if (!acc.hasOwnProperty(key)) {
acc[key] = 0
}
acc[key] += 1
return acc
}, {})
// not sure why you want the result to be multiple objects. But here you go:
const output = Object.entries(result).map(([key, value])=> ({ [key]: value }))

Create a map from your data keyed by the book names, where the corresponding values are the objects you want in the output, with the count set to zero (you can use the computed property name syntax for the object's dynamic property). Then iterate the data again to increment the counters. Finally extract the values from the map into an array:
const apple = [{"bookName" :'Harry Pottar',part:"1"},{"bookName" :'Harry Pottar',part:"2"},
{"bookName": 'LOTR',part:"1"},{"bookName": 'LOTR',part:"2"},{"bookName": 'LOTR',part:"3"}];
let map = new Map(apple.map(({bookName}) => [bookName, { [bookName]: 0 }]));
for (let {bookName} of apple) map.get(bookName)[bookName]++;
let result = Array.from(map.values());
console.log(result);

You were pretty close. You don't necessarily need to have those objects in an array though. Just have an object with the booknames as the property keys. It would make it easier to manage.
If you then want to create an array of objects from that data you can use map over the Object.entries of that object.
const apple = [{"bookName" :'Harry Pottar',part:"1"},{"bookName" :'Harry Pottar',part:"2"},{"bookName": 'LOTR',part:"1"},{"bookName": 'LOTR',part:"2"},{"bookName": 'LOTR',part:"3"}];
const out = apple.reduce((acc, { bookName }) => {
// If the property doesn't exist, create it
// and set it to zero, otherwise increment the value
// of the existing property
acc[bookName] = (acc[bookName] || 0) + 1;
return acc;
}, {});
console.log(out);
const result = Object.entries(out).map(([ key, value ]) => {
return { [key]: value };
});
console.log(result);

Related

filter array of ojects with max value in javascript

Hi I have an array of objects like below:
let tbrows = [{"rowindx":0,"speciescnt":2},{"rowindx":0,"speciescnt":3},{"rowindx":1,"speciescnt":2},{"rowindx":1,"speciescnt":3}]
I want to get the maximum value of speciecnt for each row (i.e. after filtering the array) I would like it to be
let tbrows = [{"rowindx":0,"speciescnt":3},{"rowindx":1,"speciescnt":3}];
I am using the following code that I found on the web to filter an array but it only filters on one attribute of object.
const max2 = tbrows.reduce((op, item) => op = op > item.speciescnt? op : item.speciescnt, 0);
You can also using reduce() to do it
let tbrows = [{"rowindx":0,"speciescnt":2},{"rowindx":0,"speciescnt":3},{"rowindx":1,"speciescnt":2},{"rowindx":1,"speciescnt":3}]
let result = tbrows.reduce((a,c) => {
let obj = a.find(i => i.rowindx == c.rowindx)
if(!obj){
a.push(c)
}else if(c.speciescnt > obj.speciescnt){
obj.speciescnt = c.speciescnt
}
return a
},[])
console.log(result)
Turn the array into an object (right now, you're trying to turn it into just a number). Have the object be indexed by the row index, with the associated value for that row as the highest speciecnt found so far. Then you can turn the object back into an array.
const input = [{"rowindx":0,"speciescnt":2},{"rowindx":0,"speciescnt":3},{"rowindx":1,"speciescnt":2},{"rowindx":1,"speciescnt":3}];
const grouped = {};
for (const { rowindx, speciescnt } of input) {
grouped[rowindx] = Math.max(grouped[rowindx] ?? -Infinity, speciescnt);
}
const output = Object.entries(grouped)
.map(([rowindx, speciescnt]) => ({ rowindx, speciescnt }));
console.log(output);

Find Unique value from an array based on the array's string value (Javascript)

so I want to find unique values from an array.
so for example I have this array:
const mainArr = ['shape-10983', 'size-2364', 'size-7800', 'size-4602', 'shape-11073', 'size-15027', 'size-15030', 'size-15033', 'height-3399', 'height-5884']
so I want to find the first matching value for each unique item.
for example, in the array, I have two strings with the shape prefix, six items with the size prefix, and two items with the height prefix.
so I want to output to be something like
const requiredVal = ["shape-10983", "size-2364", "height-3399"]
I want only the first value from any set of different values.
the simplest solution will be to iterate on the list and storing what you got in a dictionary
function removeSimilars(input) {
let values = {};
for (let value of input) {//iterate on the array
let key = value.splitOnLast('-')[0];//get the prefix
if (!(key in values))//if we haven't encounter the prefix yet
values[key] = value;//store that the first encounter with the prefix is with 'value'
}
return Object.values(values);//return all the values of the map 'values'
}
a shorter version will be this:
function removeSimilars(input) {
let values = {};
for (let value of input)
values[value.splitOnLast('-')[0]] ??= value;
return Object.values(values);
}
You could split the string and get the type and use it aks key for an object along with the original string as value. At result take only the values from the object.
const
data = ['shape-10983', 'size-2364', 'size-7800', 'size-4602', 'shape-11073', 'size-15027', 'size-15030', 'size-15033', 'height-3399', 'height-5884'],
result = Object.values(data.reduce((r, s) => {
const [type] = s.split('-', 1);
r[type] ??= s;
return r;
}, {}));
console.log(result);
If, as you mentioned in the comments, you have the list of prefixes already available, then all you have to do is iterate over those, to find each first element that starts with that prefix in your full list of possible values:
const prefixes = ['shape', 'size', 'height'];
const list = ['shape-10983', 'size-2364', 'size-7800', 'size-4602', 'shape-11073', 'size-15027', 'size-15030', 'size-15033', 'height-3399', 'height-5884']
function reduceTheOptions(list = [], prefixes = [], uniques = []) {
prefixes.forEach(prefix =>
uniques.push(
list.find(e => e.startsWith(prefix))
)
);
return uniques;
}
console.log(reduceTheOptions(list, prefixes));
Try this:
function getRandomSet(arr, ...prefix)
{
// the final values are load into the array result variable
result = [];
const randomItem = (array) => array[Math.floor(Math.random() * array.length)];
prefix.forEach((pre) => {
result.push(randomItem(arr.filter((par) => String(par).startsWith(pre))));
});
return result;
}
const mainArr = ['shape-10983', 'size-2364', 'size-7800', 'size-4602', 'shape-11073', 'size-15027', 'size-15030', 'size-15033', 'height-3399', 'height-5884'];
console.log("Random values: ", getRandomSet(mainArr, "shape", "size", "height"));
I modified the #ofek 's answer a bit. cuz for some reason the ??= is not working in react project.
function removeSimilars(input) {
let values = {};
for (let value of input)
if (!values[value.split("-")[0]]) {
values[value.split("-")[0]] = value;
}
return Object.values(values);
}
create a new array and loop over the first array and check the existing of element before in each iteration if not push it to the new array

Jquery check if one array contains same value

I have an simple javascript array that may contain duplicates or maynot contain duplicates.
var names = [
['aaa','pin/test1.html'],
['bbb','pin/test2.html'],
['ttt','test.html'],
['ggg','test.html'],
['yyy','un/777.html'],
['ggg','test3.html'],
['nnn','test3.html'],
['eee','n/777.html'],
['sss','pin/test1.html'],
['xxx','pin/test2.html'],
['ppp','pin/test1.html'],
];
I need to find the duplicate filepath and put their name into new array. If there is no duplicate then assign its name in first and then assign '' after two values. I could point all the codes that I have tried but it doesnt work. I accept jquery solution also. The expected outcome is this.
var outcome = [
[['aaa','sss','ppp'], 'pin/test1.html'],
[['bbb','eee','xxx'], 'pin/test2.html'],
[['ttt','ggg',''], 'test.html'],
[['yyy','',''], 'un/777.html'],
[['ggg','nnn',''], 'test3.html'],
];
What I have tried is this
for (var i = 0; i < arr.length; i++) {
var uniqueNames = [];
$.each(arr[i], function (i, el) {
if ($.inArray(el, uniqueNames) === -1) uniqueNames.push(el);
});
console.log(uniqueNames);
}
You could take a hash table and an array of empty strings and find the next slot for the value.
The array is reduced by taking an object as accumulator and a destructure array as value (the first part of the array) and key (the second part, aka filepath).
Inside of Array#reduce, a property check with the key is made and if undefined, an array with the wanted structure (array with two items, the first is an array with three emty spaces and the key) is being assigned by using a logical nullish assignment ??=.
The next line assigns the value to the next free slot, an item with an empty string.
Finally the accumulator is returned.
To get only an array as result, a conversion of the values of the object takes place.
let names = [['aaa','pin/test1.html'], ['bbb','pin/test2.html'], ['ttt','test.html'], ['ggg','test.html'], ['yyy','un/777.html'], ['ggg','test3.html'], ['nnn','test3.html'], ['eee','n/777.html'], ['sss','pin/test1.html'], ['xxx','pin/test2.html'], ['ppp','pin/test1.html']],
grouped = Object.values(names.reduce((r, [v, k]) => {
r[k] ??= [Array(3).fill(''), k];
r[k][0][r[k][0].indexOf('')] = v;
return r;
}, {}));
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
const aux = (names) => {
const hash = {};
let max = 0;
names.forEach(ele => {
if (!hash[ele[1]]) hash[ele[1]] = [];
hash[ele[1]].push(ele[0]);
max = Math.max(hash[ele[1]].length, max);
});
return Object.keys(hash).map(ele => [[...hash[ele], ...Array(max -hash[ele].length).fill("")], ele]);
}
var names = [
['aaa','pin/test1.html'],
['bbb','pin/test2.html'],
['ttt','test.html'],
['ggg','test.html'],
['yyy','un/777.html'],
['ggg','test3.html'],
['nnn','test3.html'],
['eee','n/777.html'],
['sss','pin/test1.html'],
['xxx','pin/test2.html'],
['ppp','pin/test1.html'],
];
console.log(aux(names))
This might help
You do not need jQuery for dealing with regular JS structure, you can achieve what you want with a simple code like this:
var names = [['aaa','pin/test1.html'],['bbb','pin/test2.html'],['ttt','test.html'],['ggg','test.html'],['yyy','un/777.html'],['ggg','test3.html'],['nnn','test3.html'],['eee','n/777.html'],['sss','pin/test1.html'],['xxx','pin/test2.html'],['ppp','pin/test1.html'],];
let lengthToFill = 0;
// collecting all the duplicates into a map
const pathMap = {};
names.forEach(name => {
// just in case if you're not familiar with array destructuring
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
const [pathName, path] = name;
// make sure we have an array to deal with
// just in case you're not familiar with Nullish coalescing operator (??)
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator
pathMap[path] = pathMap[path] ?? [];
pathMap[path].push(pathName);
// tracking the max number of elements we're adding into a single entry
lengthToFill = Math.max(lengthToFill, pathMap[path].length);
});
const result = Object.entries(pathMap).map(entry => {
// constructing new array entry based on the data we've collected so far
return [
entry[1].concat(Array(lengthToFill - entry[1].length).fill('')),
entry[0],
];
});
console.log(result);
This solution will work for any number of elements that you'd like to fill the array with ''. It makes sure that the length of final listing is the same for all entries.

ES6 Filtering objects by unique attribute

I've an array of errors, each error has a non-unique param attribute.
I'd like to filter the array based on whether the param has been seen before.
Something like this:
const filteredErrors = [];
let params = [];
for(let x = 0; x < errors.length; x++) {
if(!params.includes(errors[x].param)) {
params.push(errors[x].param);
filteredErrors.push(errors[x]);
}
}
But I've no idea how to do this in ES6.
I can get the unique params const filteredParams = Array.from(new Set(errors.map(error => error.param)));
but not the objects themselves.
Pretty sure this is just a weakness in my understanding of higher order functions, but I just can't grasp it
You could destrucure param, check against params and add the value to params and return true for getting the object as filtering result.
As result you get an array of first found errors of the same type.
const
params = [],
filteredErrors = errors.filter(({ param }) =>
!params.includes(param) && params.push(param));
Instead of an array you can make use of an object to keep a map of existing values and make use of filter function
let params = {};
const filteredErrors = errors.filter(error => {
if(params[error.param]) return false;
params[error.param] = true;
return true;
});
i'd probably do it like this with a reduce and no need for outside parameters:
const filteredErrors = Object.values(
errors.reduce((acc, val) => {
if (!acc[val.param]) {
acc[val.param] = val;
}
return acc;
}, {}))
basically convert it into an object keyed by the param with the object as values, only setting the key if it hasn't been set before, then back into an array of the values.
generalized like so
function uniqueBy(array, prop) {
return Object.values(
array.reduce((acc, val) => {
if (!acc[val[prop]]) {
acc[val[prop]] = val;
}
return acc;
}, {}))
}
then just do:
const filteredErrors = uniqueBy(errors, 'param');
If your param has a flag identifier if this param has been seen before then you can simply do this.
const filteredErrors = errors.filter(({ param }) => param.seen === true);
OR
const filteredErrors = errors.filter((error) => error.param.seen);
errors should be an array of objects.
where param is one of the fields of the element of array errors and seen is one of the fields of param object.
You can do it by using Array.prototype.reduce. You need to iterate through the objects in the array and keep the found params in a Set if it is not already there.
The Set.prototype.has will let you find that out. If it is not present in the Set you add it both in the Set instance and the final accumulated array, so that in the next iteration if the param is present in your Set you don't include that object:
const errors = [{param: 1, val: "err1"}, {param: 2, val: "err2"}, {param: 3, val: "err3"}, {param: 2, val: "err4"}, {param: 1, val: "err5"}];
const { filteredParams } = errors.reduce((acc, e) => {
!acc.foundParams.has(e.param) && (acc.foundParams.add(e.param) &&
acc.filteredParams.push(e));
return acc;
}, {foundParams: new Set(), filteredParams: []});
console.log(filteredParams);

Create an array from an object with with key and its suffix

Looking for help to convert/group an object to an array using a key, the key has a difference with its (-)suffix.
const obj = {
"name-1":"a",
"age-1":"20",
"email-1":"a#email.com",
"name-2":"b",
"age-2":"24",
"email-2":"b#email.com",
"name-3":"c",
"age-3":"22",
"email-3":"c#email1.com"
};
Expected result
[
{
"name":"a",
"age":"20",
"email":"a#email.com"
},
{
"name":"b",
"age":"24",
"email":"b#email.com"
},
{
"name":"c",
"age":"22",
"email":"c#email.com"
}
]
Maybe due wrong search keyword unable to find a duplicate question.
You can run a reduce on Object.entries and split the keys say name-1, age-2 etc by '-' and return an array of objects.
const obj = {
"name-1":"a",
"age-1":"20",
"email-1":"a#email.com",
"name-2":"b",
"age-2":"24",
"email-2":"b#email.com",
"name-3":"c",
"age-3":"22",
"email-3":"c#email1.com"
};
const res = Object.entries(obj).reduce((acc, [key, value]) => {
const [ k, i ] = key.split('-');
acc[i - 1] = acc[i - 1] || {};
acc[i-1][k] = value;
return acc;
}, [])
console.log(res);
At the code above inside the reduce I split the key by '-' and it gives me a key of an object and the index of the final array.
Then I check if the index i - 1 exists in the array. If not then initialized it by an empty object. Here I use i - 1 because the given object keys are starting from 1 but an array starts from 0.
Finally, I put the object value into the newly created object.
This is a nice algorithm question that I will resolve by writing it with ES6 syntax.
You can achieve this thanks to some functions such as Object.entries and reduce
Example:
const obj = {
"name-1":"a",
"age-1":"20",
"email-1":"a#email.com",
"name-2":"b",
"age-2":"24",
"email-2":"b#email.com",
"name-3":"c",
"age-3":"22",
"email-3":"c#email1.com"
};
const result = Object.entries(obj)
// Here we destructure the entry with on the left the key, and value on the right
.reduce((accumulator, [key, value]) => {
const [property, index] = key.split('-');
// Get the value currently being filled, or an empty object if it doesn't
// exist yet.
const entry = accumulator[index] || {};
accumulator[index] = {
// Spread the current entry to which we are adding
// the property to the object being filled
...entry,
// Dynamic key syntax
[property]: value,
};
return accumulator;
}, [])
// Remove "holes" from the array since it's indexed with given keys
.filter(value => value !== undefined);
console.log(result);

Categories