转换JSON格式(分组依据)

时间:2019-02-18 22:11:47

标签: javascript json

我有这样的JSON对象:

[{
    "name" : "cat",
    "value" : 17,
    "group" : "animal",
},
 {
    "name" : "dog",
    "value" : 6,
    "group" : "animal",
},
 {
    "name" : "snak",
    "value" : 2,
    "group" : "animal",
},
{
    "name" : "tesla",
    "value" : 11,
    "group" : "car",
},
{
    "name" : "bmw",
    "value" : 23,
    "group" : "car",
}]

我想通过JS将JSON转换为以下格式:

[{
  "name":"animal",
  "children":[
     {"name":"cat", "value":17},
     {"name":"dog", "value":6},
     {"name":"snak", "value":2}
]},
{
  "name":"car",
  "children":[
     {"name":"bmw", "value":11},
     {"name":"tesla", "value":23}
]}]

我尝试通过Reduce函数进行转换和过滤,但是我无法转换为这种格式。

编辑:

我测试的代码是这样。

let groupBy = function(xs, key) {
    return xs.reduce(function(rv, x) {
    (rv[x[key]] = rv[x[key]] || []).push(x);
    return rv;
    }, {});
};

let groubedByExchange=groupBy(JSON_data, 'group');

7 个答案:

答案 0 :(得分:3)

您可以构建一个数组并在该数组中搜索相同的组。

var array = [{ name: "cat", value: 17, group: "animal" }, { name: "dog", value: 6, group: "animal" }, { name: "snak", value: 2, group: "animal" }, { name: "tesla", value: 11, group: "car" }, { name: "bmw", value: 23, group: "car" }],
    result = array.reduce((r, { group: name, ...object }) => {
        var temp = r.find(o => o.name === name);
        if (!temp) r.push(temp = { name, children: [] });
        temp.children.push(object);
        return r;
    }, []);
    
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

答案 1 :(得分:3)

一个简单的解决方案是构建中间词典,然后将其转换为输出结构。

您可以使用Array.reduce()Object.entries()Array.map()来做到这一点:

const data = [
  { "name" : "cat", "value" : 17, "group" : "animal" },
  { "name" : "dog", "value" : 6, "group" : "animal" },
  { "name" : "snak", "value" : 2, "group" : "animal" },
  { "name" : "tesla", "value" : 11, "group" : "car" },
  { "name" : "bmw", "value" : 23, "group" : "car" }
];

const result = Object.entries(data.reduce((acc, { name, value, group }) => {
  acc[group] = (acc[group] || []);
  acc[group].push({ name, value });
  return acc;
}, {})).map(([key, value]) => ({ name: key, children: value }));

console.log(result);

使用扩频运算符可使它缩短一行并仍然可读:

const data = [
  { "name" : "cat", "value" : 17, "group" : "animal" },
  { "name" : "dog", "value" : 6, "group" : "animal" },
  { "name" : "snak", "value" : 2, "group" : "animal" },
  { "name" : "tesla", "value" : 11, "group" : "car" },
  { "name" : "bmw", "value" : 23, "group" : "car" }
];

const result = Object.entries(data.reduce((acc, { name, value, group }) => {
  acc[group] = [...(acc[group] || []), { name, value }];
  return acc;
}, {})).map(([key, value]) => ({ name: key, children: value }));

console.log(result);

和一个逗号分隔符一起缩短了一个衬里:

const data = [
  { "name" : "cat", "value" : 17, "group" : "animal" },
  { "name" : "dog", "value" : 6, "group" : "animal" },
  { "name" : "snak", "value" : 2, "group" : "animal" },
  { "name" : "tesla", "value" : 11, "group" : "car" },
  { "name" : "bmw", "value" : 23, "group" : "car" }
];

const result = Object.entries(data.reduce((acc, { name, value, group }) =>
  (acc[group] = [...(acc[group] || []), { name, value }], acc)
, {})).map(([key, value]) => ({ name: key, children: value }));

console.log(result);

Object.assign()可以完成同样的操作:

const data = [
  { "name" : "cat", "value" : 17, "group" : "animal" },
  { "name" : "dog", "value" : 6, "group" : "animal" },
  { "name" : "snak", "value" : 2, "group" : "animal" },
  { "name" : "tesla", "value" : 11, "group" : "car" },
  { "name" : "bmw", "value" : 23, "group" : "car" }
];

const result = Object.entries(data.reduce((acc, { name, value, group }) =>
  Object.assign(acc, { [group]: [...(acc[group] || []), { name, value }] })
, {})).map(([key, value]) => ({ name: key, children: value }));

console.log(result);

最后,虽然更长,但具有可重复使用的groupBy函数:

const data = [
  { "name" : "cat", "value" : 17, "group" : "animal" },
  { "name" : "dog", "value" : 6, "group" : "animal" },
  { "name" : "snak", "value" : 2, "group" : "animal" },
  { "name" : "tesla", "value" : 11, "group" : "car" },
  { "name" : "bmw", "value" : 23, "group" : "car" }
];

const groupBy = prop => data => {
  return data.reduce((dict, item) => {
    const { [prop]: _, ...rest } = item;
    dict[item[prop]] = [...(dict[item[prop]] || []), rest];
    return dict;
  }, {});
};

const result = Object.entries(groupBy('group')(data))
  .map(([key, value]) => ({ name: key, children: value }));

console.log(result);

答案 2 :(得分:2)

使用Array#from,Array#reduce,Array#concat,解构,扩展语法和Map。

  1. 使用地图重新组织数据结构
  2. 使用.map重组数据

const data=[{"name":"cat","value":17,"group":"animal",},{"name":"dog","value":6,"group":"animal",},{"name":"snak","value":2,"group":"animal",},{"name":"tesla","value":11,"group":"car",},{"name":"bmw","value":23,"group":"car",}];

const res = Array.from(
  data.reduce((a,{group, ...rest})=>{
    return a.set(group, [rest].concat(a.get(group)||[]));
  }, new Map())
).map(([group, children])=>({group,children}));

console.log(res);

答案 3 :(得分:1)

const arr = [{
    "name": "cat",
    "value": 17,
    "group": "animal",
  },
  {
    "name": "dog",
    "value": 6,
    "group": "animal",
  },
  {
    "name": "snak",
    "value": 2,
    "group": "animal",
  },
  {
    "name": "tesla",
    "value": 11,
    "group": "car",
  },
  {
    "name": "bmw",
    "value": 23,
    "group": "car",
  }
]

const newFormat = arr.reduce((pre, cur) => {
  const group = pre.find(grp => grp.name === cur.group)
  if (group) {
    group.children.push({
      name: cur.name,
      value: cur.value
    })
    return pre
  }

  const newGroup = {
    name: cur.group,
    children: [{
      name: cur.name,
      value: cur.value
    }]
  }
  pre.push(newGroup)
  return pre
}, [])

console.log(newFormat)

你去了。 首先,您尝试在新数组中找到该组,如果存在该组,则将其添加到该组中。如果没有,则创建group and children数组并将其推入数组

答案 4 :(得分:1)

//COMPONENT HTML FILE

<mat-sidenav-container class="sidenav-container">
	<mat-sidenav
		#drawer
		[ngClass]="{ hidden: !(isHandset$ | async) }"
		class="sidenav"
		fixedInViewport="false"
		[attr.role]="(isHandset$ | async) ? 'dialog' : 'navigation'"
		[mode]="(isHandset$ | async) ? 'over' : 'side'"
		[opened]="!(isHandset$ | async)"
	>
		<mat-toolbar>Navigation</mat-toolbar>
		<mat-nav-list>
			<a
				*ngFor="let navItem of navItems"
				routerLinkActive="active"
				mat-list-item
				routerLink="{{ navItem.path }}"
				>{{ navItem.text }}</a
			>
			<div *ngIf="!isAuthorized; else loggedIn">
				<a mat-list-item (click)="loginDialog()">Login</a>
				<a mat-list-item (click)="joinDialog()">Join</a>
			</div>
			<ng-template #loggedIn>
				<a mat-list-item href="#">Geebrox</a>
			</ng-template>
		</mat-nav-list>
	</mat-sidenav>
	<mat-sidenav-content>
		<mat-toolbar class="nav-header">
			<mat-toolbar-row [ngClass]="{ 'nav-expand': navExpand }">
				<img
					routerLink="/"
					class="logo-default"
					src="./assets/img/header/nav/nav-logo-default.jpg"
					alt="Honadon"
				/>
				<mat-chip-list *ngIf="!(isHandset$ | async)">
					<a
						*ngFor="let navItem of navItems"
						routerLinkActive="active"
						mat-button
						routerLink="{{ navItem.path }}"
						>{{ navItem.text }}</a
					>
				</mat-chip-list>
				<span class="spacer"></span>
				<mat-chip-list *ngIf="!isAuthorized && !(isHandset$ | async)">
					<mat-chip class="nav-button" (click)="loginDialog()">Login</mat-chip>
					<span class="divider">or</span>
					<mat-chip class="nav-button" (click)="joinDialog()">Join</mat-chip>
				</mat-chip-list>
				<mat-chip-list *ngIf="isAuthorized && !(isHandset$ | async)">
					<mat-chip><a href="#">sample_user</a></mat-chip>
				</mat-chip-list>
				<button
					type="button"
					aria-label="Toggle sidenav"
					mat-icon-button
					(click)="drawer.toggle()"
					*ngIf="(isHandset$ | async)"
				>
					<mat-icon aria-label="Side nav toggle icon">menu</mat-icon>
				</button>
			</mat-toolbar-row>
		</mat-toolbar>
		<ng-content></ng-content>
	</mat-sidenav-content>
</mat-sidenav-container>

答案 5 :(得分:0)

这将是我的ES6解决方案,使用map和reduce:

const data = [
  {
    name: "cat",
    value: 17,
    group: "animal"
  },
  {
    name: "dog",
    value: 6,
    group: "animal"
  },
  {
    name: "snak",
    value: 2,
    group: "animal"
  },
  {
    name: "tesla",
    value: 11,
    group: "car"
  },
  {
    name: "bmw",
    value: 23,
    group: "car"
  }
];

const grouped = data.reduce((acc, currItem) => {
  const groupKey = currItem.group;
  if (!acc[groupKey]) {
    acc[groupKey] = [currItem];
  } else {
    acc[groupKey].push(currItem);
  }
  return acc;
}, {});

const res = Object.keys(grouped).map(key => ({
  name: key,
  children: grouped[key].map(groupItem => ({
    name: groupItem.name,
    value: groupItem.value
  }))
}));

console.log(res);

检查控制台输出以查看中间结果。

我认为其他一些答案使用了不必要的find()(即O(n)),而您只需测试O(1)中是否已有当前的组密钥即可。

为清楚起见,我将分组结果存储在中间变量grouped中,但可以将其全部内联。

答案 6 :(得分:0)

您在这里。您可以使用lodash来实现相同的目的。

var data = [{
    "name": "cat",
    "value": 17,
    "group": "animal",
  },
  {
    "name": "dog",
    "value": 6,
    "group": "animal",
  },
  {
    "name": "snak",
    "value": 2,
    "group": "animal",
  },
  {
    "name": "tesla",
    "value": 11,
    "group": "car",
  },
  {
    "name": "bmw",
    "value": 23,
    "group": "car",
  }
]

var result = _(data)
  .groupBy('group')
  .map((group, name) =>
    ({
      name,
      children: _.map(group, ({
        name: name,
        value
      }) => ({
        name,
        value
      }))
    }))
  .value()

console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>