Covert key-value javascript object into array format - javascript

I am trying to access java script object which is available in the below format:
[
{'To':'A','From':'X','Weight':5},
{'To':'A','From':'Y','Weight':7},
{'To':'A','From':'Z','Weight':6},
{'To':'B','From':'X','Weight':2},
{'To':'B','From':'Y','Weight':9},
{'To':'B','From':'Z','Weight':4}
]
How can I access above object to create array like below ?
[
[ 'A', 'X', 5 ],
[ 'A', 'Y', 7 ],
[ 'A', 'Z', 6 ],
[ 'B', 'X', 2 ],
[ 'B', 'Y', 9 ],
[ 'B', 'Z', 4 ]
]

You can
Use Array.map() and Object.values():
const arrOfArrs = arr.map( Object.values );
Use Lodash's _.map() and _.values() in the same manner:
const _ = require('lodash');
. . .
const arrOfArrs = _.map( arr , _.values );
You should, however, be aware that the order in which an object's properties are iterated over (and hence returned) is not guaranteed in any way by the Ecmascript/Javascript standard. It can vary from Javascript implementation to implementation, and can even change from execution to execution. The most common order in which things are returned is insertion order, but that's not guaranteed.

Use a map function:
const l = [
{'To':'A','From':'X','Weight':5},
{'To':'A','From':'Y','Weight':7},
{'To':'A','From':'Z','Weight':6},
{'To':'B','From':'X','Weight':2},
{'To':'B','From':'Y','Weight':9},
{'To':'B','From':'Z','Weight':4}
]
const newArray = l.map(el => [el.To, el.From, el.Weight])
console.log(newArray);

Well, you can use array method .map()
let arr = [
{'To':'A','From':'Y','Weight':7},
{'To':'A','From':'Z','Weight':6}
]
let result = arr.map(Object.values)
console.log(result)

let a = [{'To':'A','From':'X','Weight':5},{'To':'A','From':'Y','Weight':7},{'To':'A','From':'Z','Weight':6},{'To':'B','From':'X','Weight':2},{'To':'B','From':'Y','Weight':9},{'To':'B','From':'Z','Weight':4}]
let array = []
for(i in a){
let temp = [a[i]['To'], a[i]['From'], a[i]['Weight']]
array.push(temp)
}
console.log(array)

You could use this to ensure the correct order of items in inner array.
let arr = [{'To':'A','From':'X','Weight':5},{'To':'A','From':'Y','Weight':7},{'To':'A','From':'Z','Weight':6},{'To':'B','From':'X','Weight':2},{'To':'B','From':'Y','Weight':9},{'To':'B','From':'Z','Weight':4}]
let result = arr.map(obj => [obj.To, obj.From, obj.Weight]);
console.log(result);

Related

How to reduce unsorted array against another sorted array, keeping sorted order?

Given an array of objects named allItems which is pre-sorted, but cannot be sorted again from the information it contains - what is an alternative implementation to the reduce function below that will retain the sorted order of allItems?
The logic below will output:
[{ id: 'd' }, { id: 'a' }, { id: 'b' }]
The desired output is:
[{ id: 'a' }, { id: 'b' }, { id: 'd' }]
// NOTE: allItems is pre-sorted, but lacks the information to re-sort it
const allItems = [{id:'a'}, {id:'b'}, {id:'c'}, {id:'d'}, {id:'e'}, {id:'f'}];
const includedIds = ['d', 'a', 'b'];
// QUESTION: How to create the same output, but in the order they appear in allItems
const unsortedIncludedItems = includedIds.reduce((accumulator, id) => {
const found = allItems.find(n => n.id === id);
if (found) accumulator.push(found);
return accumulator;
}, [])
As mentioned in response to #Ben, simply reversing the logic is a deal breaker for performance reasons.
Instead of iterating over the includedIds (in the wrong order) and seeing whether you can find them in allItems, just iterate over allItems (which is the right order) and see whether you can find their ids in includedIds:
const allItems = [{id:'a'}, {id:'b'}, {id:'c'}, {id:'d'}, {id:'e'}, {id:'f'}];
const includedIds = ['d', 'a', 'b'];
const includedItems = allItems.filter(item => includedIds.includes(item.id));
The issue you have here is that your code reverses the list. You can simply push to the front of the list instead, and the original order will be maintained.
Unfortunately, pushing to the front of a list is slower, it's O(n) rather than O(1). It looks like Array.prototype.unshift is supposed to be faster, but it's still O(n) according to this blog. Assuming that the number of found elements is small you won't notice any performance issues. In that case, replace push with unshift like so:
// NOTE: allItems is pre-sorted, but lacks the information to re-sort it
const allItems = [{id:'a'}, {id:'b'}, {id:'c'}, {id:'d'}, {id:'e'}, {id:'f'}];
const includedIds = ['d', 'a', 'b'];
// QUESTION: How to create the same output, but in the order they appear in allItems
const unsortedIncludedItems = includedIds.reduce((accumulator, id) => {
const found = allItems.find(n => n.id === id);
if (found) accumulator.unshift(found);
return accumulator;
}, [])
Otherwise, these are your options:
Create a wrapper around this object that reverses the indexes rather than the array. This can be done with a function like this:
const getFromEnd = (arr, i) => arr[arr.length - 1 - i]
Note that this can be replaced with arr.at(-i) in new browser versions (last few months). This could be encapsulated within a class if you're feeling OOP inclined.
Remember to manually invert the indices wherever you use this array (this will be bug-prone, as you may forget to invert them)
Reverse the array. As shown in this fiddle, even with 10,000 elements, the performance is not bad. Assuming this isn't a hotpath or user-interactive code, I think that even 100,000 is probably fine.
Update
Example B will use the index of the input array to sort the filtered array.
Try .filter() and .include() to get the desired objects and then .sort() by each object's string value. See Example A.
Another way is to use .flatMap() and .include() to get an array of arrays.
// each index is from the original array
[ [15, {id: 'x'}], [0, {id: 'z'}], [8, {id: 'y'}] ]
Then use .sort() on each sub-array index.
[ [0, {id: 'z'}], [8, {id: 'y'}], [15, {id: 'x'}] ]
Finally, use .flatMap() once more to extract the objects and flatten the array of arrays into an array of objects.
[ {id: 'z'}, {id: 'y'}, {id: 'x'} ]
See Example B
Example A (sort by value)
const all = [{id:'a'}, {id:'b'}, {id:'c'}, {id:'d'}, {id:'e'}, {id:'f'}];
const values = ['d', 'a', 'b'];
const sortByStringValue = (array, vArray, key) => array.filter(obj => vArray.includes(obj[key])).sort((a, b) => a[key].localeCompare(b[key]));
console.log(JSON.stringify(sortByStringValue(all, values, 'id')));
Example B (sort by index)
const all = [{id:'a'}, {id:'b'}, {id:'c'}, {id:'d'}, {id:'e'}, {id:'f'}];
const values = ['d', 'a', 'b'];
const alt = [{name:'Matt'}, {name:'Joe'}, {name:'Jane'}, {name:'Lynda'}, {name:'Shelly'}, {name:'Alice'}];
const filter = ['Shelly', 'Matt', 'Lynda'];
const sortByIndex = (array, vArray, key) => array.flatMap((obj, idx) => vArray.includes(obj[key]) ? [[idx, obj]] : []).sort((a, b) => a[0] - b[0]).flatMap(sub => [sub[1]]);
console.log(JSON.stringify(sortByIndex(all, values, 'id')));
console.log(JSON.stringify(sortByIndex(alt, filter, 'name')));
Why not just reverse the logic, Filter out the ids which not suppose to be includes.
// NOTE: allItems is pre-sorted, but lacks the information to re-sort it
const allItems = [
{ id: "a" },
{ id: "b" },
{ id: "c" },
{ id: "d" },
{ id: "e" },
{ id: "f" },
];
const includedIds = ["d", "a", "b"];
const findElms = (items, includedIds) => items.filter((n) => includedIds.includes(n.id))
console.log(findElms(allItems, includedIds));

How to filter an array of arrays and just return part of each inner array?

I have the following array of arrays:
const myArray = [ ['1', 'first'], ['2', 'second'], ['3', 'third'], ['4', 'fourth'] ]
I want to return an array like:
[ 'first', 'second', 'third', 'fourth' ]
I'm trying the following filter:
const res = myArray.filter( elm => elm[1] )
but it doesn't return the desired filter, just return the same...
.filter() is used to create a subset of an array containing elements that pass a certain requirements test.
.map() is used create a new array consisting of the results of passing the elements through a function.
The following should work as you intend:
const res = myArray.map(elm => elm[1]);

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 to add an property to an existing object inside an array

For example this is the original array
[
{name:xyz,id:123 },
{name:abc,id:232},
]
Now here is an another array
[
{value:'anything'},
{value:'anything12312'}
]
Now in new array or original array the output should be this
[
{value:'anything',name:xyz,id:123},
{value:'anything12312', name:abc,id:232}}
]
How can I achieve this
you mean like this ??
Use map,index and spread as :
let a = [
{name:"xyz",id:123 },
{name:"abc",id:232},
]
let b = [
{value:'anyrhing'},
{value:'anything12312'}
]
let res = a.map((el,idx)=> ({...el,...b[idx]}));
console.log(res)
Use object destructuring.
const arr1 = [
{name:'xyz',id:123 },
{name:'abc',id:232},
]
const arr2 = [
{value:'anyrhing'},
{value:'anything12312'}
]
const arr3 = [];
for(let i=0;i<arr1.length;i++)
arr3.push({...arr2[i],...arr1[i]});
A1=[
{name:"xyz",id:123 },
{name:"abc",id:232},
];
A2=[
{value:'anyrhing'},
{value:'anything12312'}
];
obj1={
...A2[0],
...A1[0]}
//another method to merge objects
obj2=Object.assign(A2[1],A1[1]);
//The result you needed
A3=[obj1,obj2];

How to get filtered in and out elements of array at one go in Javascript

I wonder if there is a precise way for getting filtered an unfiltered elements of an array in Javascript, I mean, like, in one go.
Currently, I use a logic like follows:
const myArray = ['a', 'b', 'c', 'd', 'e']
const filterArray = ['a', 'b']
// I want to combine those two expressions somehow
const filteredInResult = myArray.filter(e => filterArray.includes(e))
const filteredOutResult = myArray.filter(e => !filterArray.includes(e))
console.log(filteredInResult)
console.log(filteredOutResult)
I felt like a destructuring-like way might already be there to achieve it, but anyway, I prefer asking you guys if there is a way for getting filtered in & out results in one shot.
EDIT: SO keeps alerting me if this question is similar to the question here, but I used string comparison and includes for brewity above but the filtering expression may be more complex than that. So, the I must underline that the focus of the question is not on difference of two string arrays. I am leaving another example and hope the questions won't be merged :D
// A more complex use case
const myArray = [
{id: 1, value: 'a'},
{id: 2, value: 'b'},
{id: 3, value: 'c'},
{id: 4, value: 'd'},
{id: 5, value: 'e'},
]
const filterArray = ['a', 'b']
// I want to combine those two expressions somehow
const filteredInResult = myArray.filter(e => filterArray.includes(e.value))
const filteredOutResult = myArray.filter(e => !filterArray.includes(e.value))
console.log(filteredInResult)
console.log(filteredOutResult)
If you're worried about iterating twice over the myArray, you might first consider reducing the computational complexity. Because each iteration of the loops calls Array.prototype.includes, and the complexity of Array.prototype.includes is O(n), your code has an overall complexity of O(n ^ 2). (outer loop: O(n) * inner loop: O(n)). So, consider fixing that first: use a Set and Set.has, an O(1) operation, instead of an array and .includes. This is assuming that your actual filterArray is large enough that computational complexity is something to worry about - sets do have a bit of an overhead cost.
As for the other (main) part of the question, one option is to create the two result arrays outside, then push to the appropriate one while iterating:
const myArray = ['a', 'b', 'c', 'd', 'e']
const filterArray = new Set(['a', 'b'])
const filteredInResult = [];
const filteredOutResult = [];
for (const e of myArray) {
(filterArray.has(e) ? filteredInResult : filteredOutResult).push(e);
}
console.log(filteredInResult)
console.log(filteredOutResult)
Could also use reduce, though I don't think it looks very good:
const myArray = ['a', 'b', 'c', 'd', 'e']
const filterArray = new Set(['a', 'b'])
const { filteredInResult, filteredOutResult } = myArray.reduce((a, e) => {
a[filterArray.has(e) ? 'filteredInResult' : 'filteredOutResult'].push(e);
return a;
}, { filteredInResult: [], filteredOutResult: [] });
console.log(filteredInResult)
console.log(filteredOutResult)
You could use .reduce() instead of .filter(), where you use the (numeric) boolean value of includes() as the index for your accumilator like so:
const myArray = ['a', 'b', 'c', 'd', 'e'];
const filterArray = ['a', 'b'];
const [fOut, fIn] = myArray.reduce((a, n) => {
a[+filterArray.includes(n)].push(n);
return a;
}, [[], []]);
console.log(fIn);
console.log(fOut);
I could not destruct but this seems to be simpler to read than the reduce offered elsewhere
const myArray = ['a', 'b', 'c', 'd', 'e']
const filterArray = ['a', 'b']
let filteredOutResult = [];
const filteredInResult = myArray.filter(item => {
if (filterArray.includes(item)) return item;
filteredOutResult.push(item);
});
console.log(filteredInResult,filteredOutResult)
A solution with ramda's partition.
const
array = ['a', 'b', 'c', 'd', 'e'],
filter = ['a', 'b'],
[filteredInResult, filteredOutResult] = R.partition(v => filter.includes(v), array);
console.log(...filteredInResult); // a b
console.log(...filteredOutResult) // c d e
<script src="http://cdn.jsdelivr.net/ramda/latest/ramda.min.js"></script>
One native answer is to use Array#reduce:
const { in, out } = myArr.reduce((acc, v) => {
(filterArray.includes(v) ? acc.in : acc.out).push(v);
return acc;
}, { in: [], out: [] });
And then destructure the returned object

Categories