Mapping arrays into object with values as an array - javascript

I would like to create an object containing all the values from the array first and the values from second as an array of values, assuming that this key references multiple numbers such that given the arrays:
first = [3,0,5,3]
second = [1,3,10,5]
my output would be {0: 3, 3: [1,5], 5: 10}
This is what I have tried:
const newObj = {}
for(let i = 0; i < first.length; i++){
newObj[first[i]] = second[i] ? [ second[i] ] : second[i]
}
This is what I get:
{ '0': [ 3 ], '3': [ 5 ], '5': [ 10 ] }

This is the easiest method to understand as it's just the bare logic written in its entirety.
for (let i = 0; i < first.length; i++) {
if (first[i] in newObj) { // If key exists already
if (typeof newObj[first[i]] === "number") { // and if it's a number
newObj[first[i]] = [newObj[first[i]]]; // then we wrap it into an array
}
newObj[first[i]].push(second[i]); // add the new number
} else {
newObj[first[i]] = second[i]; // if it doesn't exist then add it
}
}

You can loop through all the items. You have three conditions for every array element you get. You will try to map it to a key of your new object you are creating and these will be the cases:
the key exists and value is already an array. (just push to the value)
the key exists and is not an array. (just create an array of two values)
the key does not exist. (just add a new key value pair)
const first = [3,0,5,3];
const second = [1,3,10,5];
const ans = {};
first.forEach((curr,index) => {
if(Array.isArray(ans[curr])){ ans[curr] = [...ans[curr],second[index]];
}
else{
if(ans[curr]){ans[curr] = [ans[curr],second[index]];
}
else ans[curr] = second[index];
}
});
console.log(ans);

I would collect all the values into arrays, regardless of how many there are (the transform function below does this) — having a data structure with uniform types is more predictable/easier to work with.
Then, in a subsequent step, the single element from the arrays with only one element can be extracted (the extractSingleArrayElements function below does this). See comments in the code for explanation:
Code in TypeScript Playground
'use strict';
/**
* Takes an array of keys and an array of values, and returns an object
* with values collected in arrays grouped by the keys that match their indexes
*
* #param {number[]} keys Array of integer keys
* #param {number[]} values Array of integer values
* #returns {Record<string, number[]>}
*/
function transform (keys, values) {
// Ensure that the lengths of the arrays are the same before proceeding:
if (keys.length !== values.length) throw new Error('Input lengths differ');
const result = {};
for (let i = 0; i < values.length; i += 1) {
const key = keys[i];
const value = values[i];
// Create a refrence to the array of values at the key,
// first creating and assigning it to the object at the key
// if it doesn't already exist:
const array = (result[key] ??= []);
array.push(value);
}
return result;
}
/**
* Takes an object with array values and returns a shallow copy of the object,
* replacing any array values that have only one element with the element itself
*
* #param {Record<string, number[]>} obj
* #returns {Record<string, number | number[]>}
*/
function extractSingleArrayElements (obj) {
const result = {};
for (const [key, array] of Object.entries(obj)) {
result[key] = array.length === 1 ? array[0] : array;
}
return result;
}
const keys = [3, 0, 5, 3];
const values = [1, 3, 10, 5];
const transformed = transform(keys, values);
const actual = extractSingleArrayElements(transformed);
const expected = {0: 3, 3: [1, 5], 5: 10};
console.log(JSON.stringify(actual) === JSON.stringify(expected)); // true

This is my suggestion for it, using Array.reduce, spread syntax and computed property names
first.reduce((o, k, i) => ({ ...o, [k]: [...o[k]||[], second[i]] }), {})
first = [3,0,5,3]
second = [1,3,10,5]
output = first.reduce((o, k, i) => ({ ...o, [k]: [...o[k]||[], second[i]] }), {})
console.log(output)
You can find detailed explanation on this strategy in this other answer.

Related

Can I have an object key as part of an array?

I am trying to sort some numbers, and I want to count the number of times that an array has a certain number.
My question is more about the structure of an the array than the counting the number part. I would like to build an array that looks like this below.
let numbers = [1,2,3,4,4,4,5,5,6];
How could I make the data structure below?
numbers[3].count // this will be equal to 3 after I loop through;
How do I make each part of the array have an object parameter?
Do I just loop through like so?
for (let i = 0; i < numbers.length; i++){
numbers[i] = {
count: 0
}
}
I understand this wont give me the right count, but I don't care about that part of the problem. I would like to solve that on my own. I just need to be sure that this is the correct way to add the object parameters.
I would build these functions on my own. Something like this
You can copy and paste this in the console of your browser.
// my numbers list
const numbers = [1, 2, 3, 4, 4, 4, 5, 5, 6];
// reduced to unique entries
const uniques = [...new Set(numbers)];
// function to count occurrences in my list of number
const count = (n) => numbers.filter((num) => num === n).length;
// you can test here
console.log(`counting ${uniques[4]}s`, count(uniques[4]));
// get these as object
console.log(uniques.map((unique) => ({[unique]: count(unique)})))
Simplest way to achieve this by using Array.forEach().
let numbers = [1,2,3,4,4,4,5,5,6];
const obj = {};
numbers.forEach((item) => {
obj[item] = (obj[item] || 0) + 1;
});
console.log(obj);
const numbers = [1,2,3,4,4,4,5,5,6];
const counts = {};
numbers.forEach((x) => counts[x] = (counts[x] || 0) + 1);
console.log(counts)
You can use the Array#reduce() method to group elements together into sub arrays. Since arrays have a length property that gives the the number of elements this can be applied to each group of like elements. We do not need to create a new count property.
let numbers = [1,2,3,4,4,4,5,5,6];
const freq = numbers.reduce(
(acc,cur) => ({...acc,[cur]:(acc[cur] || []).concat(cur)})
);
console.log( freq[4] );
console.log( freq[4].length );
Alternatively, you can put the numbers in an object and get all the unique elements, then for each unique element define a get property that groups like elements together using the Array#filter() method. Again, the length array property can be used to return the number of elements for each unique element.
const o = {numbers: [1,2,3,4,4,4,5,5,6]};
o.uniq = [...new Set(o.numbers)];
o.uniq.forEach(n => Object.defineProperty(
o,n,{get:() => o.numbers.filter(num => num === n)}
));
console.log( o[5] );
console.log( o[5].length );
Reduce is perfect for these kinds of problems.
const numbers = [1,2,3,4,4,4,5,5,6];
const countedObject = numbers.reduce((tmpObj, number) => {
if (!tmpObj[number]) {
tmpObj[number] = 1;
} else {
tmpObj[number] += 1;
}
return tmpObj
}, {});
console.log(countedObject);
if you feel the need to nest it further you can of course do this.
But if count is the only property you need, I'd suggest sticking to the first version.
const numbers = [1,2,3,4,4,4,5,5,6];
const countedObject = numbers.reduce((tmpObj, number) => {
if (!tmpObj[number]) {
tmpObj[number] = {count: 1};
} else {
tmpObj[number].count += 1;
}
return tmpObj
}, {});
console.log(countedObject);

How to get all the values and keys inside an array of objects separated with comma without inbuilt methods

I have tried to retrieve all the values of an plain object which returns an array separated with comma since I am using map().
var obj = { 1: 'Banana', 2: 'Orange'};
var values = Object.keys(obj).map(function(e) {
return obj[e]
}) //returns ['Banana','Orange']
Where as I need all the values and keys as string separated by comma from below object array without using Object.values() and Object.keys
input: var items = [{1:"Banana"}, {2:"Orange"}, {3:"Apple"}]
output 1: "1,2,3" ---> keys
output 2: "Banana,Orange,Apple" --> values
Problem is when the objects are inside an array I am not getting any idea how to retrieve keys and object values separately.
Kindly help!
You can use array reduce & join
Inside reduce callback use for..in to iterate the object. [[],[]] is the accumulator & in the first array keys will be collected and in second array values will be collected.
Inside the for..in push the keys & values from each object.
Once done use join with delimiter , to create the string
var items = [{
1: "Banana"
}, {
2: "Orange"
}, {
3: "Apple"
}]
let vals = items.reduce(function(acc, curr) {
for (let keys in curr) {
acc[0].push(keys);
acc[1].push(curr[keys])
}
return acc;
}, [
[],
[]
]);
console.log(vals[0].join(','), vals[1].join(','))
You can iterate over the array and in a nested for loop iterate over the object:
var items = [{1:"Banana"}, {2:"Orange"}, {3:"Apple"}];
let keys = [];
let values = [];
for (let element of items){
for (let i in element){
keys.push(i);
values.push(element[i]);
}
}
keys = keys.join(",");
values = values.join(",");
console.log(keys);
console.log(values);
Using for ... in
const items = [{
1: "Banana"
}, {
2: "Orange"
}, {
3: "Apple"
}];
let keys = '';
let values = '';
items.forEach((x) => {
for (let y in x) {
keys = `${keys}${keys.length ? ',' : ''}${y}`;
values = `${values}${values.length ? ',' : ''}${x[y]}`;
}
});
console.log(keys);
console.log(values);
Using Object.keys() and Array.reduce
const items = [{
1: "Banana"
}, {
2: "Orange"
}, {
3: "Apple"
}];
const rets = items.reduce((tmp, x) => {
Object.keys(x).forEach((y) => {
tmp.keys = `${tmp.keys}${tmp.keys.length ? ',' : ''}${y}`;
tmp.values = `${tmp.values}${tmp.values.length ? ',' : ''}${y}`;
});
return tmp;
}, {
keys: [],
values: [],
});
console.log(rets);
With the for..in and for..of functions
const items = [{1:"Banana"}, {2:"Orange"}, {3:"Apple"}];
let keys = [];
let values = [];
for(let item of items){
for(let key in item){
keys.push(key);
values.push(item[key]);
}
}
// keys == [1, 2, 3]
// values == [Banana, Orange, Apple]
After this you can use the join function
console.log(keys.join()); // Output : "1,2,3"
console.log(values.join()); // Output : "Orange,Banana,Apple"

Removing duplicate key value pair in javascript array

I have a javascript array which get created dynamically in this format.
[{prdName: "Testing2"},
{prdName: "Testing2,Testing3"},
{markets: "Testing5"},
{markets: "Testing5,Testing6"}]
I want to remove the duplicate key in the above array map and convert it into this format.
[ {prdName: "Testing2,Testing3"},
{markets: "Testing5,Testing6"} ]
Could you let me know how to achieve the same. I am working on a reactjs application.
With ES6, you could use Map with Set for unique items.
var array = [{ prdName: "Testing2" }, { prdName: "Testing2,Testing3" }, { markets: "Testing5" }, { markets: "Testing5,Testing6" }],
map = new Map,
result;
array.forEach(o => Object.keys(o).forEach(k => {
if (!map.has(k)) {
map.set(k, new Set);
}
o[k].split(',').forEach(s => map.get(k).add(s));
}));
result = [...map].map(([k, s]) => ({ [k]: [...s].join() }));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I'm assuming that you want to preserve all of the non-duplicate comma-delimited entries, not just throw away all but the last group as in the other answers. (So, for example, if the input were [{foo: "x"}, {foo:"x,y"}, {foo:"z"}] the output should be [{foo: "x,y,z"}], not [{foo:"z"}].)
var rearrange = function(input) {
var tmp = {}; // track keys and values as we find them
for (obj of input) {
var key = Object.keys(obj)[0]; // each input has one key
tmp[key] = tmp[key] || {}; // initialize an empty object at that key in tmp if necessary
var vals = obj[key].split(",");
for (v of vals) {
tmp[key][v.trim()] = 1; // keep each of the (trimmed) comma-delimited values, implicitly dropping duplicates
}
}
// now convert the tmp object into an array:
var output = [];
for (k of Object.keys(tmp)) {
var x = {};
x[k] = Object.keys(tmp[k]).join(","); // merge the values back into a comma-separated string
output.push(x);
}
return output;
}
console.log(rearrange([
{prdName: "Testing2"},
{prdName: "Testing2,Testing3"},
{markets: "Testing5"},
{markets: "Testing5,Testing6"}
]));
console.log(rearrange([
{foo: "x"},
{foo: "x,y"},
{foo: "z"},
{bar: "x,y,z"}
]));
If, however, all you need is the last instance of each key, this is pretty close to a one-liner; just use Object.assign to merge the objects:
var rearrange = function(input) {
var merged = Object.assign({},...input); // merges all input keys, overwriting early values with later ones
// convert to array (if necessary. the "merged" object might be easier to work with...):
var output=[];
for (k of Object.keys(merged)) {
var x = {};
x[k] = merged[k];
output.push(x)
}
return output;
}
console.log(rearrange([
{prdName: "Testing2"},
{prdName: "Testing2,Testing3"},
{markets: "Testing5"},
{markets: "Testing5,Testing6"}
]));
console.log(rearrange([{foo: "x"}, {foo:"x,y"}, {foo:"z"}]));
var lists =[{prdName: "Testing2"},
{prdName: "Testing2,Testing3"},
{markets: "Testing5"},
{markets: "Testing5,Testing6"}]
var newLists =[]
var keys = []
lists.forEach(item=>{
var key = Object.keys(item)[0];
if(keys.indexOf(key) === -1){
// first time the key is processed; it is stored in newLists
keys.push(key);
newLists.push(item);
}
else {
// a duplicate key is found in the array
let remove;
let values;
newLists.forEach((item2,index) => {
if (Object.keys(item2)[0] === key) {
// use of a set to have a union of values already stored for the key and the new values found for the same key using spread operator
values = new Set([...item2[key].split(","),...item[key].split(",")]);
remove = index;
}
})
newLists.splice(remove, 1);
newLists.push({[key]: Array.from(values).toString()})
}
})
console.log(newLists);

Convert set of object's into array item

I have several objects like this:
{'id[0]': 2}
{'url[0]': 11}
{'id[1]': 3}
{'url[1]': 14}
And I want to get something like this:
[{id:2, url:11}, {id:3, url:14}]
Also I have lodash in my project. Maybe lodash have some method for this?
You could use a regular expression for the keys and create a new object if necessary. Then assign the value to the key.
var data = [{ 'id[0]': 2 }, { 'url[0]': 11 }, { 'id[1]': 3 }, { 'url[1]': 14 }],
result = [];
data.forEach(function (a) {
Object.keys(a).forEach(function (k) {
var keys = k.match(/^([^\[]+)\[(\d+)\]$/);
if (keys.length === 3) {
result[keys[2]] = result[keys[2]] || {};
result[keys[2]][keys[1]] = a[k];
}
});
});
console.log(result);
This is an ES6 solution based on #NinaScholz solution.
I assume that the objects have only one property each, like the ones presented in the question.
Combine the array of objects to one large object using Object#assign, and convert to entries with Object.entries.
Iterate the array using Array#reduce.
Extract the original key an value from each entry using array
destructuring.
Extract the wanted key and index using a regex and array
destructuring.
Then create/update the new object at the index using object spread.
const data = [{ 'id[0]': 2 }, { 'url[0]': 11 }, { 'id[1]': 3 }, { 'url[1]': 14 }];
// combine to one object, and convert to entries
const result = Object.entries(Object.assign({}, ...data))
// extract the original key and value
.reduce((r, [k, value]) => {
// extract the key and index while ignoring the full match
const [, key, index] = k.match(/^([^\[]+)\[(\d+)\]$/);
// create/update the object at the index
r[index] = {...(r[index] || {}), [key]: value };
return r;
}, []);
console.log(result);
var arr = [{'id[0]': 2},
{'url[0]': 11},
{'id[1]': 3},
{'url[1]': 14}];
var result = [];
arr.forEach(function(e, i, a){
var index = +Object.keys(e)[0].split('[')[1].split(']')[0];//get the number inside []
result[index] = result[index] || {}; //if item is undefined make it empty object
result[index][Object.keys(e)[0].split('[')[0]] = e[Object.keys(e)[0]];//add item to object
})
console.log(result);
You can use for loop, .filter(), RegExp constructor with parameter "\["+i+"\]" where i is current index, Object.keys(), .reduce(), .replace() with RegExp /\[\d+\]/
var obj = [{
"id[0]": 2
}, {
"url[0]": 11
}, {
"id[1]": 3
}, {
"url[1]": 14
}];
var res = [];
for (var i = 0; i < obj.length / 2; i++) {
res[i] = obj.filter(function(o) {
return new RegExp("\[" + i + "\]").test(Object.keys(o))
})
.reduce(function(obj, o) {
var key = Object.keys(o).pop();
obj[key.replace(/\[\d+\]/, "")] = o[key];
return obj
}, {})
}
console.log(res);

Retrieving all values from a JavaScript object

This is the function I wrote to retrieve all the values in an given object.
function getValues(data){
var keys = Object.keys(data);
var values = [];
for(var i = 0, l = keys.length, key; i< l; i++){
key = keys[i];
values.push(data[key]);
}
return values;
}
Is there any builtin way to retrieve all the values in an object? Something like this exists in java for HashMaps. I know JS has a method for retrieving all the keys by doing Object.keys(obj).
Probably the most concise way of getting an array of the values contained within an object is to use Object.keys and Array.prototype.map:
obj = {
a: 1,
b: 2,
c: 3
};
values = Object.keys(obj).map(function (key) {
return obj[key];
});
Otherwise there's no standardized way of getting an array of an object's values.
For iterating, ES6 introduces a for..of loop which will iterate through an object's values:
continued from above:
for (value of obj) {
console.log(value); //1, 2, 3
}
ES7 is slated to introduce array comprehensions, so generating the values array could be written as:
continued from above:
values = [for (x of Object.keys(obj)) obj[x]];
If you're already using underscore, you can use the _.values method:
continued from above:
_.values(obj); //[1, 2, 3]
If you just want an efficient implementation for this utility function, the lodash source is:
lodash.js v2.4.1 lines 2891-2914
/**
* Creates an array composed of the own enumerable property values of `object`.
*
* #static
* #memberOf _
* #category Objects
* #param {Object} object The object to inspect.
* #returns {Array} Returns an array of property values.
* #example
*
* _.values({ 'one': 1, 'two': 2, 'three': 3 });
* // => [1, 2, 3] (property order is not guaranteed across environments)
*/
function values(object) {
var index = -1,
props = keys(object),
length = props.length,
result = Array(length);
while (++index < length) {
result[index] = object[props[index]];
}
return result;
}
You could do this, in newer Browsers:
Object.defineProperty(Object.prototype, 'values', {
get:function(){
return function(o){
var a = [];
for(var i in o){
a.push(o[i]);
}
return a;
}
}
});
var arrayOfValues = Object.values({a:'A',b:'B',c:'C'});
Really, I would just do:
function objectValues(obj, inherited){
var a = [];
for(var i in obj){
var v = obj[i];
if(inherited){
a.push(v);
}
else if(obj.hasOwnProperty(i)){
a.push(v);
}
}
return a;
}
var notInheritedArrayOfValues = objectValues({a:'A',b:'B',c:'C'});
var inheritedArrayOfValues = objectValues({a:'A',b:'B',c:'C'}, true);

Categories

Resources