This question already has answers here:
How to convert an array into an object in javascript with mapped key-value pairs?
(4 answers)
Closed 10 months ago.
What is the best way to convert this
const items = [
{ name: "Leon", url: "../poeple" },
{ name: "Bmw", url: "../car" }
];
into this using javascript :
const result = {
Leon: "../poeple",
Bmw: "../car"
};
You could just reduce the array:
const items = [{
name: "Leon",
url: "../people"
},
{
name: "Bmw",
url: "../car"
}
];
const o = items.reduce((o, el) => {
o[el.name] = el.url;
return o;
}, {});
console.log(o)
The Array.prototype.reduce method is very flexible, and might be used to reduce an array to another entity:
The reduce() method executes a user-supplied "reducer" callback function on each element of the array, in order, passing in the return value from the calculation on the preceding element. The final result of running the reducer across all elements of the array is a single value.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
You could map the objects as entries and get an object from it.
const
items = [{ name: "Leon", url: "../poeple" }, { name: "Bmw", url: "../car" }],
result = Object.fromEntries(items.map(({ name, url }) => [name, url]));
console.log(result);
There are lots of different approaches as your can see. But if you're new to JavaScript maybe a simple loop over the array to create new keys and values in a new object would be easier to understand.
const items=[{name:"Leon",url:"../poeple"},{name:"Bmw",url:"../car"}];
const out = {};
for (const obj of items) {
out[obj.name] = obj.url;
}
console.log(out);
Another way (tested on your example):
function convertit (src) {
var dest = {}, i;
for (i = 0; i < src.length; i++) {
var record = src[i];
dest[record.name] = record.url;
}
return dest;
}
Try Like This
const items = [
{ name: "Leon", url: "../poeple" },
{ name: "Bmw", url: "../car" }
];
const newObj = Object.fromEntries(items.map(ele => [ele.name,ele.url]));
console.log(newObj);
Or You can create function like this
const items = [
{ name: "Leon", url: "../poeple" },
{ name: "Bmw", url: "../car" }
];
const convertArrayToObject = (array, key) => {
const initialValue = {};
return array.reduce((obj, item) => {
return {
...obj,
[item[key]]: item.url,
};
}, initialValue);
};
console.log(convertArrayToObject(items, "name"));
Related
I'm working on a d3-force visualisation, which requires
data in a specific shape. I've got an array of objects, each with an array of tags.
nodes = [
{ name: "post1", tag_list: ["activity", "online"] },
{ name: "post2", tag_list: ["workshop", "online"] },
{ name: "post3", tag_list: ["english", "workshop"] },
...
]
To establish connections between data, I need to explicitly define an array of links:
links = [
{
source: 'post1',
target: 'post2'
},
{
source: 'post2',
target: 'post3'
},
...
]
There is no difference in similarity between links - all relationships are linear and carry the same "agency". Identical data should ideally be filtered to prevent duplicate lines.
How can I generate a link array of the previously mentioned shape from the tag_list arrays?
Here's an example of the required data structure.
--
Some context: I'm trying to visualise thematic overlaps between blog pages. All pages have an array of tags to describe them (tag_list). I wish to connect all tags within the graph. Since d3 requires verbose references to draw links (see link below), I need to compute these from the tag lists that are accessible to me.
You could collect each tag, and for each tag collect the distinct names (in a Set). When such a tag already has names associated to it, iterate those and pair it with the "current" name, putting the lexically smaller name as first pair-member. Store this pair in a map of Sets, so that they are unique.
Here is an implementation:
let nodes = [
{ name: "post1", tag_list: ["activity", "online"] },
{ name: "post2", tag_list: ["workshop", "online"] },
{ name: "post3", tag_list: ["english", "workshop"] },
];
let tags = {};
let pairs = {};
let result = [];
for (let {name, tag_list} of nodes) {
for (let tag of tag_list) {
for (let other of tags[tag] ??= new Set) {
let [source, target] = [name, other].sort();
if (!(pairs[source] ??= new Set).has(target)) {
pairs[source].add(target);
result.push({source, target});
}
}
tags[tag].add(name);
}
}
console.log(result);
You can use hash grouping approach. First make an object where keys are hashes of the links, and then use only the values as the result.
const nodes = [
{ name: "post1", tag_list: ["activity", "online"] },
{ name: "post2", tag_list: ["workshop", "online"] },
{ name: "post3", tag_list: ["online"] },
{ name: "post4", tag_list: ["workshop"] },
{ name: "post5", tag_list: ["lonely"] },
];
const hasIntersection = (arrA, arrB) => arrA.some((el) => arrB.includes(el));
const groupedByHash = nodes.reduce((acc, targetNode) => {
const commonNodes = nodes
.filter(({ tag_list }) => hasIntersection(tag_list, targetNode.tag_list))
.filter(({ name }) => name !== targetNode.name);
if (commonNodes.length < 1) return acc;
const commonLinks = commonNodes.reduce((acc, { name }) => {
const [source, target] = [name, targetNode.name].sort();
const hash = [source, target].join('---');
acc[hash] = { source, target };
return acc;
}, {});
return { ...acc, ...commonLinks };
}, {});
const result = Object.values(groupedByHash);
console.log(result);
.as-console-wrapper{min-height: 100%!important; top: 0}
In the map function... if the arrayOfObjects was not "obj.car" but was say "obj.animal" or obj.anything... I don't want to have to change that line for every array of objects I use the function on that has a different property name. Ultimately the function should take in an array of objects then return an array of values and check for duplicates then return unique array. Thanks in advance. Also I am new and I feel like I shouldn't be using two return statements within one function so any tips much appreciated.
const arrObj = [
{ car: "BMW" },
{ car: "Honda" },
{ car: "BMW" },
{ car: "BMW" },
];
const duplicates = (arrayOfObjects) => {
var finalArray = arrayOfObjects.map((obj) => {
return obj.car;
});
const uniqueSet = new Set(finalArray);
const uniqueArr = [...uniqueSet];
return uniqueArr;
};
console.log(duplicates(arrObj));
Have your duplicates function also accept a key, then use bracket notation to look up that key:
const arrObj = [
{ car: "BMW" },
{ car: "Honda" },
{ car: "BMW" },
{ car: "BMW" },
];
const duplicates = (arrayOfObjects, key) => {
var finalArray = arrayOfObjects.map((obj) => {
return obj[key];
});
const uniqueSet = new Set(finalArray);
const uniqueArr = [...uniqueSet];
return uniqueArr;
};
console.log(duplicates(arrObj, 'car'));
Consider, I have the following two arrays of objects:
const existingAndArchivedBookings =
[
{"booking_id":-2},
{"booking_id":-1},
{"booking_id":999}
]
const newAndExistingBookings =
[
{bookingId:-2, name: "name1"},
{bookingId:-3, name: "name1"},
{bookingId:-1, name: "namex"}
]
What I want to do is determine which of the bookings in the second array are new and which are existing. Any bookingId that is in both arrays is existing. Any bookingID that is in the second array but not the first is new. So, the result of the solution should be an array as follows:
[ { bookingId: -2, existing: true, name: 'name1' },
{ bookingId: -3, existing: false, name: 'name1' },
{ bookingId: -1, existing: true, name: 'namex' } ]
I have a solution (which I'll post as an answer), but I think there's probably a more efficient way of doing it. Good luck.
If you want a non-R answer: you can use a simple map to iterate over the data, compare the booking ids in both arrays (with some), and return a new array of objects.
const existingAndArchivedBookings = [{booking_id:-2},{booking_id:-1},{booking_id:999}];
const newAndExistingBookings = [{bookingId:-2, name: "name1"},{bookingId:-3, name: "name1"},{bookingId:-1, name: "namex"}];
function testBookings(arr1, arr2) {
return arr2.map(({ bookingId, name }) => {
const existing = arr1.some(obj => obj.booking_id === bookingId);
return { bookingId, existing, name };
});
}
const out = testBookings(existingAndArchivedBookings, newAndExistingBookings);
console.log(out);
You can greatly simplify it using Array.prototype.reduce to form the result of the comparisons between the 2 arrays and Array.prototype.findIndex to test whether the object in the second array is present in the first array:
const existingAndArchivedBookings =
[
{"booking_id":-2},
{"booking_id":-1},
{"booking_id":999}
]
const newAndExistingBookings =
[
{bookingId:-2, name: "name1"},
{bookingId:-3, name: "name1"},
{bookingId:-1, name: "namex"}
]
const res = newAndExistingBookings.reduce((acc, ele) => {
const idx = existingAndArchivedBookings.findIndex(b => b.booking_id === ele.bookingId);
let existing = false;
if(idx >=0 ){
existing = true;
}
return acc.concat({bookingId : `${ele.bookingId}`, existing: `${existing}`, name: `${ele.name}`});
}, []);
console.log(res);
Here's what I came up with, which seems a bit long winded
const R = require('ramda')
const existingAndArchivedBookings = [{"booking_id":-2},{"booking_id":-1},{"booking_id":999}]
const newAndExistingBookings = [{bookingId:-2, name: "name1"}, {bookingId:-3, name: "name1"}, {bookingId:-1, name: "namex"}]
const existingAndArchivedKeys = existingAndArchivedBookings.map(value => value.booking_id)
const newAndExistingKeys = newAndExistingBookings.map(value => value.bookingId)
const existingKeys = existingAndArchivedKeys.filter(key => newAndExistingKeys.includes(key))
const newKeys = newAndExistingKeys.filter(key => !existingAndArchivedKeys.includes(key))
const existingBookingIds = existingKeys.map(key => {
return {bookingId: key, existing: true}
})
const newBookingIds = newKeys.map(key => {
return {bookingId: key, existing: false}
})
const allArray = R.concat(newAndExistingBookings, R.concat(existingBookingIds, newBookingIds))
console.log(R.values(R.reduceBy(R.mergeLeft, {}, R.prop('bookingId'), allArray)))
I have one question about filter array in forEach. So I would like filter (bigger than in example) array using outside variable filterKey. I think that my function is correct by after filtered newArr is undefined. Could you explain what is incorrect?
var filterKey = 123456,
var array = [{
ratings:{ users:[id: 123456]}, user: xyz
},
{
ratings:{users:[id:9787389023]}, user:zyx
}],
And my filter function
var newArr = array.forEach((ele) =>
ele.ratings.users.filter((newEl) =>
newEl.id == filterKey))
Use array.filter method
let array = [
{
id: 123456, user: 'xyz'
},
{
id:9787389023, user: 'zyx'
},
{
id: 123456, user: 'che'
}
]
let newArray = array.filter((element) => element.id === 123456)
console.log(newArray)
Use .filter and you'll be able to filter your result set without using foreach since it'll loop across the array.
var find = 123456;
var arr = [
{
id: 123456,
user: 'john'
},
{
id: 9787389023,
user: 'leah'
}
];
var results = arr.filter(function(node) {
return node.id === find;
});
console.log(results);
See jsfiddle here: https://jsfiddle.net/remenyLx/2/
I have data that contains objects that each have an array of images. I want only the first image of each object.
var data1 = [
{
id: 1,
images: [
{ name: '1a' },
{ name: '1b' }
]
},
{
id: 2,
images: [
{ name: '2a' },
{ name: '2b' }
]
},
{
id: 3
},
{
id: 4,
images: []
}
];
var filtered = [];
var b = data1.forEach((element, index, array) => {
if(element.images && element.images.length)
filtered.push(element.images[0].name);
});
console.log(filtered);
The output needs to be flat:
['1a', '2a']
How can I make this prettier?
I'm not too familiar with JS map, reduce and filter and I think those would make my code more sensible; the forEach feels unnecessary.
First you can filter out elements without proper images property and then map it to new array:
const filtered = data1
.filter(e => e.images && e.images.length)
.map(e => e.images[0].name)
To do this in one loop you can use reduce function:
const filtered = data1.reduce((r, e) => {
if (e.images && e.images.length) {
r.push(e.images[0].name)
}
return r
}, [])
You can use reduce() to return this result.
var data1 = [{
id: 1,
images: [{
name: '1a'
}, {
name: '1b'
}]
}, {
id: 2,
images: [{
name: '2a'
}, {
name: '2b'
}]
}, {
id: 3
}, {
id: 4,
images: []
}];
var result = data1.reduce(function(r, e) {
if (e.hasOwnProperty('images') && e.images.length) r.push(e.images[0].name);
return r;
}, [])
console.log(result);
All answers are creating NEW arrays before projecting the final result : (filter and map creates a new array each) so basically it's creating twice.
Another approach is only to yield expected values :
Using iterator functions
function* foo(g)
{
for (let i = 0; i < g.length; i++)
{
if (g[i]['images'] && g[i]["images"].length)
yield g[i]['images'][0]["name"];
}
}
var iterator = foo(data1) ;
var result = iterator.next();
while (!result.done)
{
console.log(result.value)
result = iterator.next();
}
This will not create any additional array and only return the expected values !
However if you must return an array , rather than to do something with the actual values , then use other solutions suggested here.
https://jsfiddle.net/remenyLx/7/