Creating new objects by dynamically enumerating keys of others - javascript

I have an array of objects with named keys as follows:
Array [
{
'postal1': 111, // number
'postal2': 222, // number
'town': 'London', // string
},
{},{},... // Objects with same properties
]
The array is created after DOM and populated dynamically from a backend, so from the very beginning it's just an empty array.
Having all this I want to create a new object where keys will be town and their values will be an array of two other keys postal1 and postal2.
Object {
'London': [111,222],
'town...': [rand,rand],
}
How one could make it in the most efficient way?

You can use Array.reduce() to generate an object from the array:
const data = [{
'postal1': 111, // number
'postal2': 222, // number
'town': 'London', // string
}];
const result = data
.reduce((r, { town, postal1, postal2 }) => {
r[town] = [postal1, postal2];
return r;
}, {});
console.log(result);

Try this:
var A= [
{
'postal1': 1110, // number
'postal2': 2220, // number
'town': 'A', // string
},
{
'postal1': 1111, // number
'postal2': 2221, // number
'town': 'B', // string
},
{
'postal1': 1112, // number
'postal2': 2222, // number
'town': 'C', // string
}
];
var desiredResult={};
A.forEach(function(a){
desiredResult[a.town]=[a.postal1, a.postal2];
});
console.log(desiredResult);
But this is faster than all:
var i=0,
result={};
for(; i<A.length; i++) result[A[i].town]=[A[i].postal1, A[i].postal2];

Related

JavaScript concat/merge 2 different objects, both containing arrays for each string

I want to concat 2 objects into the first object, but making sure the array elements stay in the same x-positions, like:
object.name[x],
object.age[x].
Also, I don't want any duplicates to be overwritten. Every entry should be preserved.
var users = {
name : [joe , tim],
age : [20 , 21]
}
var usersTemp = {
name : [bob , joe],
age : [22 , 23]
}
Result should be saved into (expand) the existing 'users' object:
var users = {
name : [joe , tim , bob , joe],
age : [20 , 21 , 22 , 23 ]
}
PS: I'm new to javascript, and since I cant find any examples I am wondering if this approach even makes sense? The list is SUPER long (over 50k entries).
You can use Array Destructuring to achieve this task if you don't want to append in the original array.
var users = {
name: ["joe", "tim"],
age: [20, 21],
};
var usersTemp = {
name: ["bob", "joe"],
age: [22, 23],
};
const result = {
name: [...users.name, ...usersTemp.name],
age: [...users.age, ...usersTemp.age],
};
console.log(result);
If you want to add in the same array then assign the result in the same variable users
var users = {
name: ["joe", "tim"],
age: [20, 21],
};
var usersTemp = {
name: ["bob", "joe"],
age: [22, 23],
};
users = {
name: [...users.name, ...usersTemp.name],
age: [...users.age, ...usersTemp.age],
};
console.log(users);
If there are multiple properties in both object that need to merge
var users = {
name: ["joe", "tim"],
age: [20, 21],
};
var usersTemp = {
name: ["bob", "joe"],
age: [22, 23],
};
for (let key in users) {
users[key].push(...usersTemp[key]);
}
console.log(users);
You could make a function that takes two objects with arbitrary keys and builds your new object with the concatenated values. You can then grab the entries of one of your objects using Object.entries(), which will give you a [[key, value], ...] pair array representation of your object:
[
['name', ['joe', 'tim']], // <- inner key-value pair array
['age', [20, 21]]
]
Using this array, you can then use the .map() method on it, to convert each inner key-value pair into a new key-value pair array, where the key remains, but the value is a concatenated version of the current array value with its corresponding array value from the other object you want to merge with. Once you have your modified the entries array, you can use Object.fromEntries() on this array to build your new object from the entries array:
const users = { name: ['joe', 'tim'], age: [20, 21] };
const usersTemp = { name: ['bob', 'ted'], age: [22, 23] };
function concatObjVals(obj1, obj2) {
return Object.fromEntries(
Object.entries(obj1).map(([key, arr]) => [
key,
arr.concat(obj2[key])
])
);
}
console.log(concatObjVals(users, usersTemp));
This could be written in a more concise manner if you use arrow-functions:
const concatObjVals = (obj1, obj2) =>
Object.fromEntries(Object.entries(obj1).map(([key, arr]) => [key, arr.concat(obj2[key])]));
If you find it easier to understand, you can use a regular for..in loop instead of Object.entries() and .map() to build your new object. The below for..in loop will iterate through all the keys in your first object (ie: users), and add that key to a new object result. It will also set the value to be the current value of the key from the first object, concatenated with the array value from the second object for the current key:
const users = { name: ['joe', 'tim'], age: [20, 21] };
const usersTemp = { name: ['bob', 'ted'], age: [22, 23] };
function concatObjVals(obj1, obj2) {
const result = {};
for(const key in obj1) {
result[key] = obj1[key].concat(obj2[key]);
}
return result;
}
console.log(concatObjVals(users, usersTemp));
var users = {
name : ['joe' , 'tim'],
age : [20 , 21]
}
var usersTemp = {
name : ['bob' , 'joe'],
age : [22 , 23]
}
usersTemp.name.push(...users.name)
usersTemp.age.push(...users.age)
console.log(usersTemp)
// output
// { name: Array ["bob", "joe", "joe", "tim"], age: Array [22, 23, 20, 21] }

Reduce Array of Array of Objects Using Ramda

I have an array of array of objects. I want to reduce that to an array of object and adding one more property to each object.
The sample input is:
const data = [
[
{name:"a", val:5},
{name:"b", val:10},
{name:"c", val:20},
{name:"d", val:50},
{name:"e", val:100}
],
[
{name:"a", val:0},
{name:"b", val:20},
{name:"c", val:30},
{name:"d", val:40},
{name:"e", val:10}
],
[
{name:"a", val:60},
{name:"b", val:50},
{name:"c", val:40},
{name:"d", val:70},
{name:"e", val:30}
]
];
And the Output should be:
[{name: 'a', val: 65, rank: 'si'},
{name: 'b', val: 80, rank: 'dp'},
{name: 'c', val: 90, rank: 'en'}
{name: 'd', val: 160, rank: 'fr'}]
Rank is static text means for a, it will always be "si"
How can I achieve this using ramda?
You can convert flatten all sub arrays to a single array, group by the name, and then map the groups, and reduce each group to a single object using R.mergeWithKey to add the val property. Convert back to an array using R.values, and map to add the static ranks property by name.
Note that you must create a Map or a dictionary object to take the rank by name from.
const { mergeWithKey, pipe, flatten, groupBy, prop, map, reduce, values } = R
const ranks = new Map([['a', 'si'], ['b', 'dp'], ['c', 'en'], ['d', 'fr']])
// merge deep and combine val property values
const combine = mergeWithKey((k, l, r) => k == 'val' ? l + r : r)
const mergeData = pipe(
flatten, // flatten to a single array
groupBy(prop('name')), // group by the name
map(reduce(combine, {})), // combine each group to a single object
values, // convert back to array
map(o => ({ ...o, rank: ranks.get(o.name) })), // add the static rank property
)
const data = [[{"name":"a","val":5},{"name":"b","val":10},{"name":"c","val":20},{"name":"d","val":50},{"name":"e","val":100}],[{"name":"a","val":0},{"name":"b","val":20},{"name":"c","val":30},{"name":"d","val":40},{"name":"e","val":10}],[{"name":"a","val":60},{"name":"b","val":50},{"name":"c","val":40},{"name":"d","val":70},{"name":"e","val":30}]]
const results = mergeData(data)
console.log(results)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js" integrity="sha512-rZHvUXcc1zWKsxm7rJ8lVQuIr1oOmm7cShlvpV0gWf0RvbcJN6x96al/Rp2L2BI4a4ZkT2/YfVe/8YvB2UHzQw==" crossorigin="anonymous"></script>

How can I know how many matches I have between two arrays (one nested inside an object)?

I need to know how many matches I have between people.compare array and names array, then create a new object including this data.
names = [ 'Juan', 'Old']
people = [
{compare: ['Juan', 'Old'], Age: 70},
{compare: ['Juan', 'Young'], Age: 20}
]
Expected output:
peopleNew = [
{compare: ['Juan', 'Old'], 'Age': 70, 'MATCHES': 2},
{compare: ['Juan', 'Young'], 'Age': 20, 'MATCHES': 1}
]
This code loops through each array and uses Array.includes() to check each item for equality:
const peopleNew = people.map(obj => {
let matches = 0;
obj.compare.forEach(name => {
if (names.includes(name)) {
matches++; // Increase count by 1
}
})
// Create new object from original with new 'matches' property
return Object.assign({}, obj, { matches });
});

How to chunk an array based on another array of numbers?

I have an array of data, and an array of objects:
const data = ['1', '2', '2'];
const objlist = [{name : 'dummy'} , {name: 'new'}, {name : 'news'}, {name : 'place'}, ...]; // 5 objects
I want to chunk the objects in objlist by the numbers in data so that I get the follow result:
result = [
[{name:'dummy'}],
[{name:'new'}, {name:'news'}],
[{name : 'place'}, ...]
]
As you can see, it should be of the form:
[[{obj1}], [{obj2}, {obj3}], [{obj4}, {obj5}]]
You could push sliced parts to the result.
let array = [1, 2, 2],
objlist = [{ name: 'dummy' }, { name: 'new' }, { name: 'news' }, { name: 'place' }, { name: 'place' }],
result = [],
i = 0,
j = 0;
while (j < array.length) {
result.push(objlist.slice(i, i += array[j++]));
}
console.log(result);
You can loop through your array of numbers and for each number n use .splice(0, n) to get an array chunk from your array of objects. This will modify the array in-place, allowing your next .splice() to get the next consecutive object. For each .splice() you perform, you can .push() this into a resulting array.
See example below:
function partition([...arr], chunks) {
const res = [];
for(const n of chunks)
res.push(arr.splice(0, n)); // +n to turn the string number into a number (splice will do this conversion for you but you can take care of it explicitly as well)
return res;
}
const chunkArr = ['1', '2', '2'];
const arr = [{ name : 'dummy' }, {name: 'new' }, { name : 'news'},{name : 'place'}, {name : 'foo'}];
console.log(partition(arr, chunkArr));
Above I'm using partition([...arr], chunks) which uses the destructuring assignment syntax to perform a shallow copy of your input array. This way when you modify it inside your function using .splice(), it won't change the passed-in array.

I have two arrays, how do I find matchings elements and perform some action? (lodash)

var array1 = [{Age: 24, Name: "Test", StudentID: 101, Checked: false}, {Age:25, Name: "Test", StudentID: 102, Checked: false}];
var array2 = [{ID: 101}];
If any element in array1 has a property of StudentID that is equal to an ID property present in array2 I'd like to set the Checked property in array1 to true.
Any tips? I'd like to do this without writing nested _.each statements.
This is my first take; however, I believe _.some performs an interation anyway.
_.each($scope.array1, function(element1) {
if(_.some($scope.array2, { ID: element1.ID })) {
element1.Checked = true;
}
});
You''ll have to use two loops, since you have two arrays of random length. But you don't have to nest them. Create a map from the array of IDs and then check the index.
var availableIDs = array2.map(function ( item ) { return item.ID; });
array1.forEach(function ( item ) {
if (availableIDs.indexOf(item.StudentID) !== -1) item.Checked = true;
});
Using lodash, use a sequence in which you create a map of items in array1, using _.indexBy(). Create an array of ids from array2 using _.pluck(), and use them with _.at() to get the selected items. Iterate the returned objects using _.forEach() to set the Checked property to true, and .commit() to apply the changes:
function checkById(items, selected) {
_(items) // start chained sequence
.indexBy('StudentID') // create a map of student objects by ids
.at(_.pluck(selected, 'ID')) // create an array of IDs from the objects in the selected array
.forEach(function(item) { // change the items Checked to true
item.Checked = true;
})
.commit(); // executes the chained sequence
}
var array1 = [{
Age: 24,
Name: "Test1",
StudentID: 101,
Checked: false
}, {
Age: 25,
Name: "Test2",
StudentID: 102,
Checked: false
}, {
Age: 22,
Name: "Test3",
StudentID: 103,
Checked: false
}, {
Age: 28,
Name: "Test4",
StudentID: 104,
Checked: false
}];
var array2 = [{
ID: 101
}, {
ID: 104
}];
checkById(array1, array2);
console.table(array1);
document.getElementById('demo').innerText = JSON.stringify(array1, null, ' ');
<script src="https://cdn.jsdelivr.net/lodash/3.10.1/lodash.min.js"></script>
<pre id="demo"></pre>
using a simple mapping function you can compose an easy search through all objects
var array1 = [{Age: 24, Name: "Test", StudentID: 101, Checked: false}, {Age:25, Name: "Test", StudentID: 102, Checked: false}];
var array2 = [{ID: 101}];
function search(studentList,searchQuery) {
var results = [];
studentList.forEach(function(student,sIndex) {
searchQuery.forEach(function(search,qIndex) {
if(search.ID == student.StudentID) {
results.push(student);
}
});
})
return results;
}
search(array1,array2);
what the forEach function does is iterate over each element, passing along the object of the index it's iterating, and the index that object is at.
By having a double nested map it's easy to iterate over the objects and then compare them according to the rules you define.
Then by using a scoped variable you can push matching values into that array, giving you a nice, neat clean result to return.
Now please mind, this is not the most efficient way to handle this. You could do a test which arary is longest and have that one iterate the least time.
So if there are more students than search parameters iterate the students once. If there are more search parameters than students, iterate the search paramateters once.
also you could chooose to 'prefilter" the arrays by sorting them on the index you wish to sort on, skip the ones you don't need by simple min/max exclusion and such.
But you'd be better off using a database query for searching with large quantities of data.
But if you only have a dataset of about a 1000 or so this will suffice.
Try this snippet:
_.each(array1, function (el) {
el.Checked = !!(JSON.stringify(array2).indexOf(el.StudentID) + 1) || el.Checked;
});
Or, you can do without lo-dash.js(with pure JavaScript)
var array1 = [{Age: 24, Name: "Test", StudentID: 101, Checked: false}, {Age:25, Name: "Test", StudentID: 102, Checked: false}];
var array2 = [{ID: 101}];
var students = array1.filter(function(data){
var isTrue = !!(JSON.stringify(array2).indexOf(data.StudentID)+1);
data.Checked = isTrue || data.Checked;
return isTrue;
})
console.log(students)

Categories